Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4755](https://github.com/open-telemetry/opentelemetry-python/pull/4755))
- logs: extend Logger.emit to accept separated keyword arguments
([#4737](https://github.com/open-telemetry/opentelemetry-python/pull/4737))
- logs: Added different warning classes as necessary for log class deprecation
([#4771](https://github.com/open-telemetry/opentelemetry-python/pull/4771))

## Version 1.37.0/0.58b0 (2025-09-11)

Expand Down
14 changes: 12 additions & 2 deletions opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@

from opentelemetry.sdk._logs._internal import (
LogData,
LogDeprecatedInitWarning,
LogLimitsInitDeprecatedWarning,
LogRecordInitDeprecatedWarning,
LogDataInitDeprecatedWarning,
LogRecordContextDeprecatedWarning,
InMemoryLogExporterDeprecatedWarning,
ConsoleLogExporterDeprecatedWarning,
LogDroppedAttributesWarning,
Logger,
LoggerProvider,
Expand All @@ -33,6 +38,11 @@
"LogLimits",
"LogRecord",
"LogRecordProcessor",
"LogDeprecatedInitWarning",
"LogLimitsInitDeprecatedWarning",
"LogRecordInitDeprecatedWarning",
"LogDroppedAttributesWarning",
"LogDataInitDeprecatedWarning",
"LogRecordContextDeprecatedWarning",
"InMemoryLogExporterDeprecatedWarning",
"ConsoleLogExporterDeprecatedWarning",
]
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,19 @@ class LogDroppedAttributesWarning(UserWarning):
warnings.simplefilter("once", LogDroppedAttributesWarning)


class LogDeprecatedInitWarning(UserWarning):
class LogLimitsInitDeprecatedWarning(UserWarning):
"""Custom warning to indicate deprecated LogLimits init was used.

This class is used to filter and handle these specific warnings separately
from other warnings, ensuring that they are only shown once without
interfering with default user warnings.
"""


warnings.simplefilter("once", LogLimitsInitDeprecatedWarning)


class LogRecordInitDeprecatedWarning(UserWarning):
"""Custom warning to indicate deprecated LogRecord init was used.

This class is used to filter and handle these specific warnings separately
Expand All @@ -93,7 +105,55 @@ class LogDeprecatedInitWarning(UserWarning):
"""


warnings.simplefilter("once", LogDeprecatedInitWarning)
warnings.simplefilter("once", LogRecordInitDeprecatedWarning)


class LogDataInitDeprecatedWarning(UserWarning):
"""Custom warning to indicate deprecated LogData init was used.

This class is used to filter and handle these specific warnings separately
from other warnings, ensuring that they are only shown once without
interfering with default user warnings.
"""


warnings.simplefilter("once", LogDataInitDeprecatedWarning)


class LogRecordContextDeprecatedWarning(UserWarning):
"""Custom warning to indicate LogRecord was initialized without using context.

This class is used to filter and handle these specific warnings separately
from other warnings, ensuring that they are only shown once without
interfering with default user warnings.
"""


warnings.simplefilter("once", LogRecordContextDeprecatedWarning)


class InMemoryLogExporterDeprecatedWarning(UserWarning):
"""Custom warning to indicate InMemoryLogExporter is deprecated.

This class is used to filter and handle these specific warnings separately
from other warnings, ensuring that they are only shown once without
interfering with default user warnings.
"""


warnings.simplefilter("once", LogRecordContextDeprecatedWarning)


class ConsoleLogExporterDeprecatedWarning(UserWarning):
"""Custom warning to indicate ConsoleLogExporter is deprecated.

This class is used to filter and handle these specific warnings separately
from other warnings, ensuring that they are only shown once without
interfering with default user warnings.
"""


warnings.simplefilter("once", ConsoleLogExporterDeprecatedWarning)


class LogLimits:
Expand Down Expand Up @@ -147,6 +207,12 @@ def __init__(
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
)

warnings.warn(
"LogLimits will be deprecated in 1.39.0 and then renamed to LogRecordLimits",
LogLimitsInitDeprecatedWarning,
stacklevel=0,
)

def __repr__(self):
return f"{type(self).__name__}(max_attributes={self.max_attributes}, max_attribute_length={self.max_attribute_length})"

Expand Down Expand Up @@ -234,13 +300,18 @@ def __init__( # pylint:disable=too-many-locals
limits: LogLimits | None = None,
event_name: str | None = None,
):
warnings.warn(
"LogRecord will be substituted in 1.39.0 by ReadWriteLogRecord and ReadableLogRecord",
LogRecordInitDeprecatedWarning,
stacklevel=0,
)
if not context:
context = get_current()

if trace_id or span_id or trace_flags:
warnings.warn(
"LogRecord init with `trace_id`, `span_id`, and/or `trace_flags` is deprecated since 1.35.0. Use `context` instead.",
LogDeprecatedInitWarning,
LogRecordContextDeprecatedWarning,
stacklevel=2,
)

Expand Down Expand Up @@ -358,6 +429,11 @@ def __init__(
log_record: LogRecord,
instrumentation_scope: InstrumentationScope,
):
warnings.warn(
"LogData will be substituted in 1.39.0 by ReadWriteLogRecord and ReadableLogRecord",
LogDataInitDeprecatedWarning,
stacklevel=0,
)
self.log_record = log_record
self.instrumentation_scope = instrumentation_scope

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import enum
import logging
import sys
import warnings
from os import environ, linesep
from typing import IO, Callable, Optional, Sequence

Expand All @@ -26,7 +27,12 @@
detach,
set_value,
)
from opentelemetry.sdk._logs import LogData, LogRecord, LogRecordProcessor
from opentelemetry.sdk._logs import (
LogData,
ConsoleLogExporterDeprecatedWarning,
LogRecord,
LogRecordProcessor,
)
from opentelemetry.sdk._shared_internal import BatchProcessor, DuplicateFilter
from opentelemetry.sdk.environment_variables import (
OTEL_BLRP_EXPORT_TIMEOUT,
Expand Down Expand Up @@ -93,6 +99,12 @@ def __init__(
self.out = out
self.formatter = formatter

warnings.warn(
"ConsoleLogExporter will be deprecated in 1.39.0 and then renamed to ConsoleLogRecordExporter",
ConsoleLogExporterDeprecatedWarning,
stacklevel=0,
)

def export(self, batch: Sequence[LogData]):
for data in batch:
self.out.write(self.formatter(data.log_record))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

import threading
import typing
import warnings

from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs import LogData, InMemoryLogExporterDeprecatedWarning
from opentelemetry.sdk._logs.export import LogExporter, LogExportResult


Expand All @@ -32,6 +33,12 @@ def __init__(self):
self._lock = threading.Lock()
self._stopped = False

warnings.warn(
"InMemoryLogExporter will be deprecated in 1.39.0 and then renamed to InMemoryLogRecordExporter",
InMemoryLogExporterDeprecatedWarning,
stacklevel=0,
)

def clear(self) -> None:
with self._lock:
self._logs.clear()
Expand Down
107 changes: 107 additions & 0 deletions opentelemetry-sdk/tests/logs/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import os
import time
import unittest
import warnings
from concurrent.futures import ThreadPoolExecutor
from sys import version_info
from unittest.mock import Mock, patch
Expand All @@ -26,6 +27,8 @@
from opentelemetry._logs import SeverityNumber
from opentelemetry.sdk import trace
from opentelemetry.sdk._logs import (
ConsoleLogExporterDeprecatedWarning,
InMemoryLogExporterDeprecatedWarning,
LogData,
LoggerProvider,
LoggingHandler,
Expand Down Expand Up @@ -647,3 +650,107 @@ def formatter(record): # pylint: disable=unused-argument
exporter.export([EMPTY_LOG])

mock_stdout.write.assert_called_once_with(mock_record_str)

def test_console_log_exporter_deprecated_warning(self):
"""Test that ConsoleLogExporter initialization emits a deprecation warning."""
with warnings.catch_warnings(record=True) as cw:
warnings.simplefilter("always")
ConsoleLogExporter()

# Check that at least one ConsoleLogExporterDeprecatedWarning was emitted
console_warnings = [
w
for w in cw
if isinstance(w.message, ConsoleLogExporterDeprecatedWarning)
]
self.assertGreater(
len(console_warnings),
0,
"Expected at least one ConsoleLogExporterDeprecatedWarning",
)

# Check the message content of the ConsoleLogExporterDeprecatedWarning
warning_message = str(console_warnings[0].message)
self.assertIn(
"ConsoleLogExporter will be deprecated in 1.39.0 and then renamed to ConsoleLogRecordExporter",
warning_message,
)

def test_console_log_exporter_deprecated_warning_once(self):
"""Test that ConsoleLogExporter deprecation warning is only shown once due to simplefilter('once')."""
with warnings.catch_warnings(record=True) as cw:
# Multiple instantiations should only warn once due to simplefilter("once")
for _ in range(10):
ConsoleLogExporter()

# Check that exactly one ConsoleLogExporterDeprecatedWarning was emitted
console_warnings = [
w
for w in cw
if isinstance(w.message, ConsoleLogExporterDeprecatedWarning)
]
self.assertEqual(
len(console_warnings),
1,
"Expected exactly one ConsoleLogExporterDeprecatedWarning due to simplefilter('once')",
)

# Check the message content
warning_message = str(console_warnings[0].message)
self.assertIn(
"ConsoleLogExporter will be deprecated in 1.39.0 and then renamed to ConsoleLogRecordExporter",
warning_message,
)


class TestInMemoryLogExporterDeprecation(unittest.TestCase):
def test_in_memory_log_exporter_deprecated_warning(self):
"""Test that InMemoryLogExporter initialization emits a deprecation warning."""
with warnings.catch_warnings(record=True) as cw:
warnings.simplefilter("always")
InMemoryLogExporter()

# Check that at least one InMemoryLogExporterDeprecatedWarning was emitted
in_memory_warnings = [
w
for w in cw
if isinstance(w.message, InMemoryLogExporterDeprecatedWarning)
]
self.assertGreater(
len(in_memory_warnings),
0,
"Expected at least one InMemoryLogExporterDeprecatedWarning",
)

# Check the message content of the InMemoryLogExporterDeprecatedWarning
warning_message = str(in_memory_warnings[0].message)
self.assertIn(
"InMemoryLogExporter will be deprecated in 1.39.0 and then renamed to InMemoryLogRecordExporter",
warning_message,
)

def test_in_memory_log_exporter_deprecated_warning_once(self):
"""Test that InMemoryLogExporter deprecation warning is only shown once due to simplefilter('once')."""
with warnings.catch_warnings(record=True) as cw:
# Multiple instantiations should only warn once due to simplefilter("once")
for _ in range(10):
InMemoryLogExporter()

# Check that exactly one InMemoryLogExporterDeprecatedWarning was emitted
in_memory_warnings = [
w
for w in cw
if isinstance(w.message, InMemoryLogExporterDeprecatedWarning)
]
self.assertEqual(
len(in_memory_warnings),
1,
"Expected exactly one InMemoryLogExporterDeprecatedWarning due to simplefilter('once')",
)

# Check the message content
warning_message = str(in_memory_warnings[0].message)
self.assertIn(
"InMemoryLogExporter will be deprecated in 1.39.0 and then renamed to InMemoryLogRecordExporter",
warning_message,
)
43 changes: 42 additions & 1 deletion opentelemetry-sdk/tests/logs/test_log_limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
# limitations under the License.

import unittest
import warnings
from unittest.mock import patch

from opentelemetry.sdk._logs import LogLimits
from opentelemetry.sdk._logs import LogLimits, LogLimitsInitDeprecatedWarning
from opentelemetry.sdk._logs._internal import (
_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT,
)
Expand Down Expand Up @@ -70,3 +71,43 @@ def test_invalid_env_vars_raise(self):
str(error.exception),
f"Unexpected error message for {env_var}={bad_value}",
)

def test_log_limits_init_deprecated_warning(self):
"""Test that LogLimits initialization emits a deprecation warning."""
with warnings.catch_warnings(record=True) as cw:
warnings.simplefilter("always")
LogLimits()

self.assertEqual(len(cw), 1)
self.assertIsInstance(cw[-1].message, LogLimitsInitDeprecatedWarning)
self.assertIn(
"LogLimits will be deprecated in 1.39.0 and then renamed to LogRecordLimits",
str(cw[-1].message),
)

def test_log_limits_init_deprecated_warning_with_params(self):
"""Test that LogLimits initialization with parameters still emits a deprecation warning."""
with warnings.catch_warnings(record=True) as cw:
warnings.simplefilter("always")
LogLimits(max_attributes=10, max_attribute_length=100)

self.assertEqual(len(cw), 1)
self.assertIsInstance(cw[-1].message, LogLimitsInitDeprecatedWarning)
self.assertIn(
"LogLimits will be deprecated in 1.39.0 and then renamed to LogRecordLimits",
str(cw[-1].message),
)

def test_log_limits_init_deprecated_warning_once(self):
"""Test that LogLimits deprecation warning is only shown once due to simplefilter('once')."""
with warnings.catch_warnings(record=True) as cw:
# Multiple instantiations should only warn once due to simplefilter("once")
for _ in range(10):
LogLimits()

self.assertEqual(len(cw), 1)
self.assertIsInstance(cw[-1].message, LogLimitsInitDeprecatedWarning)
self.assertIn(
"LogLimits will be deprecated in 1.39.0 and then renamed to LogRecordLimits",
str(cw[-1].message),
)
Loading
Loading