diff --git a/CHANGELOG.md b/CHANGELOG.md index c36a9984bac..b2155602a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4791](https://github.com/open-telemetry/opentelemetry-python/pull/4791)) - [BREAKING] Remove LogData and extend SDK LogRecord to have instrumentation scope ([#4676](https://github.com/open-telemetry/opentelemetry-python/pull/4676)) +- [BREAKING] Rename several classes from Log to LogRecord + ([#4647](https://github.com/open-telemetry/opentelemetry-python/pull/4647)) **Migration Guide:** @@ -32,12 +34,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ```python # Before from opentelemetry.sdk._logs import LogData - def export(self, batch: Sequence[LogData]) -> LogExportResult: + def export(self, batch: Sequence[LogData]) -> LogRecordExportResult: ... # After from opentelemetry.sdk._logs import ReadableLogRecord - def export(self, batch: Sequence[ReadableLogRecord]) -> LogExportResult: + def export(self, batch: Sequence[ReadableLogRecord]) -> LogRecordExportResult: ... ``` @@ -65,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `log_record.log_record` - The API LogRecord (contains body, severity, attributes, etc.) - `log_record.resource` - The Resource - `log_record.instrumentation_scope` - The InstrumentationScope (now included, was in LogData before) - - `log_record.limits` - The LogLimits + - `log_record.limits` - The LogRecordLimits ## Version 1.38.0/0.59b0 (2025-10-16) diff --git a/docs/conf.py b/docs/conf.py index ee5cc1188de..261fd0f00a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -145,8 +145,11 @@ "py:class", "opentelemetry.proto.collector.metrics.v1.metrics_service_pb2.ExportMetricsServiceRequest", ), - ("py:class", "opentelemetry.sdk._logs._internal.export.LogExporter"), - ("py:class", "opentelemetry.sdk._logs._internal.export.LogExportResult"), + ("py:class", "opentelemetry.sdk._logs._internal.export.LogRecordExporter"), + ( + "py:class", + "opentelemetry.sdk._logs._internal.export.LogRecordExportResult", + ), ( "py:class", "opentelemetry.proto.collector.logs.v1.logs_service_pb2.ExportLogsServiceRequest", diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py index 06ff06a9e02..ef147274418 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py @@ -45,7 +45,7 @@ from opentelemetry.proto.resource.v1.resource_pb2 import ( Resource as PB2Resource, ) -from opentelemetry.sdk._logs import LogLimits, ReadWriteLogRecord +from opentelemetry.sdk._logs import LogRecordLimits, ReadWriteLogRecord from opentelemetry.sdk.resources import Resource as SDKResource from opentelemetry.sdk.util.instrumentation import InstrumentationScope from opentelemetry.trace import ( @@ -667,7 +667,7 @@ def _get_test_logs_dropped_attributes() -> List[ReadWriteLogRecord]: attributes={"a": 1, "b": "c", "user_id": "B121092"}, ), resource=SDKResource({"first_resource": "value"}), - limits=LogLimits(max_attributes=1), + limits=LogRecordLimits(max_attributes=1), instrumentation_scope=InstrumentationScope( "first_name", "first_version" ), diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py index fd0009b1528..63d8ac9cfb0 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py @@ -29,7 +29,10 @@ LogsServiceStub, ) from opentelemetry.sdk._logs import ReadableLogRecord -from opentelemetry.sdk._logs.export import LogExporter, LogExportResult +from opentelemetry.sdk._logs.export import ( + LogRecordExporter, + LogRecordExportResult, +) from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE, @@ -44,11 +47,11 @@ class OTLPLogExporter( - LogExporter, + LogRecordExporter, OTLPExporterMixin[ Sequence[ReadableLogRecord], ExportLogsServiceRequest, - LogExportResult, + LogRecordExportResult, LogsServiceStub, ], ): @@ -100,7 +103,7 @@ def __init__( timeout=timeout or environ_timeout, compression=compression, stub=LogsServiceStub, - result=LogExportResult, + result=LogRecordExportResult, channel_options=channel_options, ) @@ -112,7 +115,7 @@ def _translate_data( def export( # type: ignore [reportIncompatibleMethodOverride] self, batch: Sequence[ReadableLogRecord], - ) -> Literal[LogExportResult.SUCCESS, LogExportResult.FAILURE]: + ) -> Literal[LogRecordExportResult.SUCCESS, LogRecordExportResult.FAILURE]: return OTLPExporterMixin._export(self, batch) def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py index 461ea0aee74..be86e5b0cf5 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py @@ -81,7 +81,7 @@ ) from opentelemetry.proto.resource.v1.resource_pb2 import Resource # noqa: F401 from opentelemetry.sdk._logs import ReadableLogRecord -from opentelemetry.sdk._logs.export import LogExportResult +from opentelemetry.sdk._logs.export import LogRecordExportResult from opentelemetry.sdk._shared_internal import DuplicateFilter from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER, @@ -132,7 +132,7 @@ ) ExportResultT = TypeVar( "ExportResultT", - LogExportResult, + LogRecordExportResult, MetricExportResult, SpanExportResult, ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py index d64a601bbb4..9fc739522dc 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py @@ -63,7 +63,7 @@ # The below tests use this test SpanExporter and Spans, but are testing the -# underlying behavior in the mixin. A MetricExporter or LogExporter could +# underlying behavior in the mixin. A MetricExporter or LogRecordExporter could # just as easily be used. class OTLPSpanExporterForTesting( SpanExporter, diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py index 93b28d4a5c5..b120a2cca45 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py @@ -36,8 +36,8 @@ ) from opentelemetry.sdk._logs import ReadableLogRecord from opentelemetry.sdk._logs.export import ( - LogExporter, - LogExportResult, + LogRecordExporter, + LogRecordExportResult, ) from opentelemetry.sdk._shared_internal import DuplicateFilter from opentelemetry.sdk.environment_variables import ( @@ -71,7 +71,7 @@ _MAX_RETRYS = 6 -class OTLPLogExporter(LogExporter): +class OTLPLogExporter(LogRecordExporter): def __init__( self, endpoint: Optional[str] = None, @@ -176,17 +176,19 @@ def _export( ) return resp - def export(self, batch: Sequence[ReadableLogRecord]) -> LogExportResult: + def export( + self, batch: Sequence[ReadableLogRecord] + ) -> LogRecordExportResult: if self._shutdown: _logger.warning("Exporter already shutdown, ignoring batch") - return LogExportResult.FAILURE + return LogRecordExportResult.FAILURE serialized_data = encode_logs(batch).SerializeToString() deadline_sec = time() + self._timeout for retry_num in range(_MAX_RETRYS): resp = self._export(serialized_data, deadline_sec - time()) if resp.ok: - return LogExportResult.SUCCESS + return LogRecordExportResult.SUCCESS # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff. backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2) if ( @@ -200,7 +202,7 @@ def export(self, batch: Sequence[ReadableLogRecord]) -> LogExportResult: resp.status_code, resp.text, ) - return LogExportResult.FAILURE + return LogRecordExportResult.FAILURE _logger.warning( "Transient error %s encountered while exporting logs batch, retrying in %.2fs.", resp.reason, @@ -210,7 +212,7 @@ def export(self, batch: Sequence[ReadableLogRecord]) -> LogExportResult: if shutdown: _logger.warning("Shutdown in progress, aborting retry.") break - return LogExportResult.FAILURE + return LogRecordExportResult.FAILURE def force_flush(self, timeout_millis: float = 10_000) -> bool: """Nothing is buffered in this exporter, so this method does nothing.""" diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py index 3dd1c5fdc00..31e824a980f 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py @@ -40,7 +40,7 @@ ExportLogsServiceRequest, ) from opentelemetry.sdk._logs import ReadWriteLogRecord -from opentelemetry.sdk._logs.export import LogExportResult +from opentelemetry.sdk._logs.export import LogRecordExportResult from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER, OTEL_EXPORTER_OTLP_CERTIFICATE, @@ -454,7 +454,8 @@ def test_2xx_status_code(self, mock_otlp_metric_exporter): """ self.assertEqual( - OTLPLogExporter().export(MagicMock()), LogExportResult.SUCCESS + OTLPLogExporter().export(MagicMock()), + LogRecordExportResult.SUCCESS, ) @patch.object(Session, "post") @@ -470,7 +471,7 @@ def test_retry_timeout(self, mock_post): # Set timeout to 1.5 seconds self.assertEqual( exporter.export(self._get_sdk_log_data()), - LogExportResult.FAILURE, + LogRecordExportResult.FAILURE, ) after = time.time() # First call at time 0, second at time 1, then an early return before the second backoff sleep b/c it would exceed timeout. diff --git a/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py b/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py index d1e8c4e39f6..02d665f8b5a 100644 --- a/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py +++ b/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py @@ -4,14 +4,14 @@ from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler from opentelemetry.sdk._logs.export import ( - InMemoryLogExporter, + InMemoryLogRecordExporter, SimpleLogRecordProcessor, ) def _set_up_logging_handler(level): logger_provider = LoggerProvider() - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() processor = SimpleLogRecordProcessor(exporter=exporter) logger_provider.add_log_record_processor(processor) handler = LoggingHandler(level=level, logger_provider=logger_provider) diff --git a/opentelemetry-sdk/pyproject.toml b/opentelemetry-sdk/pyproject.toml index 4b1bbb409a4..1ced888c8ce 100644 --- a/opentelemetry-sdk/pyproject.toml +++ b/opentelemetry-sdk/pyproject.toml @@ -49,7 +49,7 @@ parentbased_traceidratio = "opentelemetry.sdk.trace.sampling:ParentBasedTraceIdR sdk_logger_provider = "opentelemetry.sdk._logs:LoggerProvider" [project.entry-points.opentelemetry_logs_exporter] -console = "opentelemetry.sdk._logs.export:ConsoleLogExporter" +console = "opentelemetry.sdk._logs.export:ConsoleLogRecordExporter" [project.entry-points.opentelemetry_meter_provider] sdk_meter_provider = "opentelemetry.sdk.metrics:MeterProvider" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 60640739e3b..59fa1ce43dd 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -39,7 +39,10 @@ from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk._events import EventLoggerProvider from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, LogExporter +from opentelemetry.sdk._logs.export import ( + BatchLogRecordProcessor, + LogRecordExporter, +) from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, @@ -97,7 +100,7 @@ Type[SpanExporter], Type[MetricExporter], Type[MetricReader], - Type[LogExporter], + Type[LogRecordExporter], ], Mapping[str, Any], ] @@ -250,7 +253,7 @@ def _init_metrics( def _init_logging( - exporters: dict[str, Type[LogExporter]], + exporters: dict[str, Type[LogRecordExporter]], resource: Resource | None = None, setup_logging_handler: bool = True, exporter_args_map: ExporterArgsMap | None = None, @@ -309,7 +312,7 @@ def _import_exporters( ) -> tuple[ dict[str, Type[SpanExporter]], dict[str, Union[Type[MetricExporter], Type[MetricReader]]], - dict[str, Type[LogExporter]], + dict[str, Type[LogRecordExporter]], ]: trace_exporters = {} metric_exporters = {} @@ -345,7 +348,7 @@ def _import_exporters( ) in _import_config_components( log_exporter_names, "opentelemetry_logs_exporter" ): - if issubclass(exporter_impl, LogExporter): + if issubclass(exporter_impl, LogRecordExporter): log_exporters[exporter_name] = exporter_impl else: raise RuntimeError(f"{exporter_name} is not a log exporter") diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py index db15568842d..ec0b3dfb23e 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py @@ -18,18 +18,22 @@ LoggerProvider, LoggingHandler, LogLimits, + LogRecordDroppedAttributesWarning, + LogRecordLimits, LogRecordProcessor, ReadableLogRecord, ReadWriteLogRecord, ) __all__ = [ - "LogDroppedAttributesWarning", "Logger", "LoggerProvider", "LoggingHandler", "LogLimits", + "LogRecordLimits", "LogRecordProcessor", + "LogDroppedAttributesWarning", + "LogRecordDroppedAttributesWarning", "ReadableLogRecord", "ReadWriteLogRecord", ] diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py index 23beff2d5c6..d775dd44555 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py @@ -28,6 +28,8 @@ from time import time_ns from typing import Any, Callable, Tuple, Union, cast, overload # noqa +from typing_extensions import deprecated + from opentelemetry._logs import Logger as APILogger from opentelemetry._logs import LoggerProvider as APILoggerProvider from opentelemetry._logs import ( @@ -67,7 +69,7 @@ def default(self, o): return super().default(o) -class LogDroppedAttributesWarning(UserWarning): +class LogRecordDroppedAttributesWarning(UserWarning): """Custom warning to indicate dropped log attributes due to limits. This class is used to filter and handle these specific warnings separately @@ -76,10 +78,17 @@ class LogDroppedAttributesWarning(UserWarning): """ -warnings.simplefilter("once", LogDroppedAttributesWarning) +warnings.simplefilter("once", LogRecordDroppedAttributesWarning) + + +@deprecated( + "Use LogRecordDroppedAttributesWarning. Since logs are not stable yet this WILL be removed in future releases." +) +class LogDroppedAttributesWarning(LogRecordDroppedAttributesWarning): + pass -class LogLimits: +class LogRecordLimits: """This class is based on a SpanLimits class in the Tracing module. This class represents the limits that should be enforced on recorded data such as events, links, attributes etc. @@ -159,6 +168,13 @@ def _from_env_if_absent( return value +@deprecated( + "Use LogRecordLimits. Since logs are not stable yet this WILL be removed in future releases." +) +class LogLimits(LogRecordLimits): + pass + + @dataclass(frozen=True) class ReadableLogRecord: """Readable LogRecord should be kept exactly in-sync with ReadWriteLogRecord, only difference is the frozen=True param.""" @@ -166,7 +182,7 @@ class ReadableLogRecord: log_record: LogRecord resource: Resource instrumentation_scope: InstrumentationScope | None = None - limits: LogLimits | None = None + limits: LogRecordLimits | None = None @property def dropped_attributes(self) -> int: @@ -226,7 +242,7 @@ class ReadWriteLogRecord: log_record: LogRecord resource: Resource | None = Resource.create({}) instrumentation_scope: InstrumentationScope | None = None - limits: LogLimits = field(default_factory=LogLimits) + limits: LogRecordLimits = field(default_factory=LogRecordLimits) def __post_init__(self): self.log_record.attributes = BoundedAttributes( @@ -241,7 +257,7 @@ def __post_init__(self): if self.dropped_attributes > 0: warnings.warn( "Log record attributes were dropped due to limits", - LogDroppedAttributesWarning, + LogRecordDroppedAttributesWarning, stacklevel=2, ) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index d14d3417402..ad79f3e687f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -20,6 +20,8 @@ from os import environ, linesep from typing import IO, Callable, Optional, Sequence +from typing_extensions import deprecated + from opentelemetry.context import ( _SUPPRESS_INSTRUMENTATION_KEY, attach, @@ -51,12 +53,20 @@ _logger.addFilter(DuplicateFilter()) +class LogRecordExportResult(enum.Enum): + SUCCESS = 0 + FAILURE = 1 + + +@deprecated( + "Use LogRecordExportResult. Since logs are not stable yet this WILL be removed in future releases." +) class LogExportResult(enum.Enum): SUCCESS = 0 FAILURE = 1 -class LogExporter(abc.ABC): +class LogRecordExporter(abc.ABC): """Interface for exporting logs. Interface to be implemented by services that want to export logs received in their own format. @@ -65,7 +75,9 @@ class LogExporter(abc.ABC): """ @abc.abstractmethod - def export(self, batch: Sequence[ReadableLogRecord]) -> LogExportResult: + def export( + self, batch: Sequence[ReadableLogRecord] + ) -> LogRecordExportResult: """Exports a batch of logs. Args: batch: The list of `ReadableLogRecord` objects to be exported @@ -81,8 +93,15 @@ def shutdown(self): """ -class ConsoleLogExporter(LogExporter): - """Implementation of :class:`LogExporter` that prints log records to the +@deprecated( + "Use LogRecordExporter. Since logs are not stable yet this WILL be removed in future releases." +) +class LogExporter(LogRecordExporter): + pass + + +class ConsoleLogRecordExporter(LogRecordExporter): + """Implementation of :class:`LogRecordExporter` that prints log records to the console. This class can be used for diagnostic purposes. It prints the exported @@ -103,18 +122,25 @@ def export(self, batch: Sequence[ReadableLogRecord]): for log_record in batch: self.out.write(self.formatter(log_record)) self.out.flush() - return LogExportResult.SUCCESS + return LogRecordExportResult.SUCCESS def shutdown(self): pass +@deprecated( + "Use ConsoleLogRecordExporter. Since logs are not stable yet this WILL be removed in future releases." +) +class ConsoleLogExporter(ConsoleLogRecordExporter): + pass + + class SimpleLogRecordProcessor(LogRecordProcessor): """This is an implementation of LogRecordProcessor which passes - received logs directly to the configured LogExporter, as soon as they are emitted. + received logs directly to the configured LogRecordExporter, as soon as they are emitted. """ - def __init__(self, exporter: LogExporter): + def __init__(self, exporter: LogRecordExporter): self._exporter = exporter self._shutdown = False @@ -152,7 +178,7 @@ def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=n class BatchLogRecordProcessor(LogRecordProcessor): """This is an implementation of LogRecordProcessor which creates batches of - received logs and sends them to the configured LogExporter. + received logs and sends them to the configured LogRecordExporter. `BatchLogRecordProcessor` is configurable with the following environment variables which correspond to constructor parameters: @@ -167,7 +193,7 @@ class BatchLogRecordProcessor(LogRecordProcessor): def __init__( self, - exporter: LogExporter, + exporter: LogRecordExporter, schedule_delay_millis: float | None = None, max_export_batch_size: int | None = None, export_timeout_millis: float | None = None, diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py index 0911a500909..a724f81d89d 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py @@ -15,12 +15,17 @@ import threading import typing -from opentelemetry.sdk._logs._internal import ReadableLogRecord -from opentelemetry.sdk._logs.export import LogExporter, LogExportResult +from typing_extensions import deprecated +from opentelemetry.sdk._logs import ReadableLogRecord +from opentelemetry.sdk._logs.export import ( + LogRecordExporter, + LogRecordExportResult, +) -class InMemoryLogExporter(LogExporter): - """Implementation of :class:`.LogExporter` that stores logs in memory. + +class InMemoryLogRecordExporter(LogRecordExporter): + """Implementation of :class:`.LogRecordExporter` that stores logs in memory. This class can be used for testing purposes. It stores the exported logs in a list in memory that can be retrieved using the @@ -42,12 +47,19 @@ def get_finished_logs(self) -> typing.Tuple[ReadableLogRecord, ...]: def export( self, batch: typing.Sequence[ReadableLogRecord] - ) -> LogExportResult: + ) -> LogRecordExportResult: if self._stopped: - return LogExportResult.FAILURE + return LogRecordExportResult.FAILURE with self._lock: self._logs.extend(batch) - return LogExportResult.SUCCESS + return LogRecordExportResult.SUCCESS def shutdown(self) -> None: self._stopped = True + + +@deprecated( + "Use InMemoryLogRecordExporter. Since logs are not stable yet this WILL be removed in future releases." +) +class InMemoryLogExporter(InMemoryLogRecordExporter): + pass diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py index 37a9eca7a08..2edcf7e9e82 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py @@ -15,21 +15,29 @@ from opentelemetry.sdk._logs._internal.export import ( BatchLogRecordProcessor, ConsoleLogExporter, + ConsoleLogRecordExporter, LogExporter, LogExportResult, + LogRecordExporter, + LogRecordExportResult, SimpleLogRecordProcessor, ) # The point module is not in the export directory to avoid a circular import. from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( InMemoryLogExporter, + InMemoryLogRecordExporter, ) __all__ = [ "BatchLogRecordProcessor", "ConsoleLogExporter", + "ConsoleLogRecordExporter", "LogExporter", + "LogRecordExporter", "LogExportResult", + "LogRecordExportResult", "SimpleLogRecordProcessor", "InMemoryLogExporter", + "InMemoryLogRecordExporter", ] diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index c18981df928..7570ed3a74f 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -34,8 +34,8 @@ from opentelemetry.sdk._logs._internal.export import _logger from opentelemetry.sdk._logs.export import ( BatchLogRecordProcessor, - ConsoleLogExporter, - InMemoryLogExporter, + ConsoleLogRecordExporter, + InMemoryLogRecordExporter, SimpleLogRecordProcessor, ) from opentelemetry.sdk.environment_variables import ( @@ -62,7 +62,7 @@ class TestSimpleLogRecordProcessor(unittest.TestCase): def test_simple_log_record_processor_default_level(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() logger_provider = LoggerProvider() logger_provider.add_log_record_processor( @@ -89,7 +89,7 @@ def test_simple_log_record_processor_default_level(self): ) def test_simple_log_record_processor_custom_level(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() logger_provider = LoggerProvider() logger_provider.add_log_record_processor( @@ -129,7 +129,7 @@ def test_simple_log_record_processor_custom_level(self): ) def test_simple_log_record_processor_trace_correlation(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() logger_provider = LoggerProvider() logger_provider.add_log_record_processor( @@ -192,7 +192,7 @@ def test_simple_log_record_processor_trace_correlation(self): ) def test_simple_log_record_processor_shutdown(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() logger_provider = LoggerProvider() logger_provider.add_log_record_processor( @@ -224,7 +224,7 @@ def test_simple_log_record_processor_shutdown(self): self.assertEqual(len(finished_logs), 0) def test_simple_log_record_processor_different_msg_types(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor(exporter) provider = LoggerProvider() @@ -266,7 +266,7 @@ def test_simple_log_record_processor_custom_single_obj(self): Tests that special-case handling for logging a single non-string object is correctly applied. """ - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor(exporter) provider = LoggerProvider() @@ -310,7 +310,7 @@ def test_simple_log_record_processor_custom_single_obj(self): def test_simple_log_record_processor_different_msg_types_with_formatter( self, ): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor(exporter) provider = LoggerProvider() @@ -364,7 +364,7 @@ def test_simple_log_record_processor_different_msg_types_with_formatter( # to run after the end of the test. class TestBatchLogRecordProcessor(unittest.TestCase): def test_emit_call_log_record(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = Mock(wraps=BatchLogRecordProcessor(exporter)) provider = LoggerProvider() provider.add_log_record_processor(log_record_processor) @@ -378,7 +378,7 @@ def test_emit_call_log_record(self): log_record_processor.shutdown() def test_with_multiple_threads(self): # pylint: disable=no-self-use - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() batch_processor = BatchLogRecordProcessor( exporter, max_queue_size=3000, @@ -438,7 +438,7 @@ def test_logging_lib_not_invoked_in_batch_log_record_emit(self): # pylint: disa sdk_logger.removeHandler(handler) def test_args(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor( exporter, max_queue_size=1024, @@ -473,7 +473,7 @@ def test_args(self): }, ) def test_env_vars(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor(exporter) self.assertEqual( log_record_processor._batch_processor._exporter, exporter @@ -493,7 +493,7 @@ def test_env_vars(self): log_record_processor.shutdown() def test_args_defaults(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor(exporter) self.assertEqual( log_record_processor._batch_processor._exporter, exporter @@ -522,7 +522,7 @@ def test_args_defaults(self): }, ) def test_args_env_var_value_error(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() _logger.disabled = True log_record_processor = BatchLogRecordProcessor(exporter) _logger.disabled = False @@ -544,7 +544,7 @@ def test_args_env_var_value_error(self): log_record_processor.shutdown() def test_args_none_defaults(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() log_record_processor = BatchLogRecordProcessor( exporter, max_queue_size=None, @@ -570,7 +570,7 @@ def test_args_none_defaults(self): log_record_processor.shutdown() def test_validation_negative_max_queue_size(self): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() self.assertRaises( ValueError, BatchLogRecordProcessor, @@ -643,7 +643,7 @@ def test_export(self): # pylint: disable=no-self-use "first_name", "first_version" ), ) - exporter = ConsoleLogExporter() + exporter = ConsoleLogRecordExporter() # Mocking stdout interferes with debugging and test reporting, mock on # the exporter instance instead. @@ -664,7 +664,9 @@ def formatter(record): # pylint: disable=unused-argument return mock_record_str mock_stdout = Mock() - exporter = ConsoleLogExporter(out=mock_stdout, formatter=formatter) + exporter = ConsoleLogRecordExporter( + out=mock_stdout, formatter=formatter + ) exporter.export([EMPTY_LOG]) mock_stdout.write.assert_called_once_with(mock_record_str) diff --git a/opentelemetry-sdk/tests/logs/test_handler.py b/opentelemetry-sdk/tests/logs/test_handler.py index c5db462bb9d..b9f1edf49a1 100644 --- a/opentelemetry-sdk/tests/logs/test_handler.py +++ b/opentelemetry-sdk/tests/logs/test_handler.py @@ -404,7 +404,7 @@ def test_handler_root_logger_with_disabled_sdk_does_not_go_into_recursion_error( def test_otel_attribute_count_limit_respected_in_logging_handler(self): """Test that OTEL_ATTRIBUTE_COUNT_LIMIT is properly respected by LoggingHandler.""" # Create a new LoggerProvider within the patched environment - # This will create LogLimits() that reads from the environment variable + # This will create LogRecordLimits() that reads from the environment variable logger_provider = LoggerProvider() processor = FakeProcessor() logger_provider.add_log_record_processor(processor) @@ -443,7 +443,7 @@ def test_otel_attribute_count_limit_respected_in_logging_handler(self): def test_otel_attribute_count_limit_includes_code_attributes(self): """Test that OTEL_ATTRIBUTE_COUNT_LIMIT applies to all attributes including code attributes.""" # Create a new LoggerProvider within the patched environment - # This will create LogLimits() that reads from the environment variable + # This will create LogRecordLimits() that reads from the environment variable logger_provider = LoggerProvider() processor = FakeProcessor() logger_provider.add_log_record_processor(processor) diff --git a/opentelemetry-sdk/tests/logs/test_log_limits.py b/opentelemetry-sdk/tests/logs/test_log_limits.py index 82a7ce9b4d6..63ea2e63542 100644 --- a/opentelemetry-sdk/tests/logs/test_log_limits.py +++ b/opentelemetry-sdk/tests/logs/test_log_limits.py @@ -15,7 +15,7 @@ import unittest from unittest.mock import patch -from opentelemetry.sdk._logs import LogLimits +from opentelemetry.sdk._logs import LogRecordLimits from opentelemetry.sdk._logs._internal import ( _DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT, ) @@ -27,20 +27,20 @@ class TestLogLimits(unittest.TestCase): def test_log_limits_repr_unset(self): - expected = f"LogLimits(max_attributes={_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT}, max_attribute_length=None)" - limits = str(LogLimits()) + expected = f"LogRecordLimits(max_attributes={_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT}, max_attribute_length=None)" + limits = str(LogRecordLimits()) self.assertEqual(expected, limits) def test_log_limits_max_attributes(self): expected = 1 - limits = LogLimits(max_attributes=1) + limits = LogRecordLimits(max_attributes=1) self.assertEqual(expected, limits.max_attributes) def test_log_limits_max_attribute_length(self): expected = 1 - limits = LogLimits(max_attribute_length=1) + limits = LogRecordLimits(max_attribute_length=1) self.assertEqual(expected, limits.max_attribute_length) @@ -62,7 +62,7 @@ def test_invalid_env_vars_raise(self): with self.assertRaises(ValueError) as error, patch.dict( "os.environ", {env_var: bad_value}, clear=True ): - LogLimits() + LogRecordLimits() expected_msg = f"{env_var} must be a non-negative integer but got {bad_value}" self.assertEqual( diff --git a/opentelemetry-sdk/tests/logs/test_log_record.py b/opentelemetry-sdk/tests/logs/test_log_record.py index 5019672ec3c..3f9c4763018 100644 --- a/opentelemetry-sdk/tests/logs/test_log_record.py +++ b/opentelemetry-sdk/tests/logs/test_log_record.py @@ -20,8 +20,8 @@ from opentelemetry.attributes import BoundedAttributes from opentelemetry.context import get_current from opentelemetry.sdk._logs import ( - LogDroppedAttributesWarning, - LogLimits, + LogRecordDroppedAttributesWarning, + LogRecordLimits, ReadableLogRecord, ReadWriteLogRecord, ) @@ -100,7 +100,7 @@ def test_log_record_dropped_attributes_empty_limits(self): def test_log_record_dropped_attributes_set_limits_max_attribute(self): attr = {"key": "value", "key2": "value2"} - limits = LogLimits( + limits = LogRecordLimits( max_attributes=1, ) @@ -115,7 +115,7 @@ def test_log_record_dropped_attributes_set_limits_max_attribute_length( ): attr = {"key": "value", "key2": "value2"} expected = {"key": "v", "key2": "v"} - limits = LogLimits( + limits = LogRecordLimits( max_attribute_length=1, ) @@ -133,7 +133,7 @@ def test_log_record_dropped_attributes_set_limits_max_attribute_length( def test_log_record_dropped_attributes_set_limits(self): attr = {"key": "value", "key2": "value2"} expected = {"key2": "v"} - limits = LogLimits( + limits = LogRecordLimits( max_attributes=1, max_attribute_length=1, ) @@ -151,7 +151,7 @@ def test_log_record_dropped_attributes_set_limits(self): def test_log_record_dropped_attributes_set_limits_warning_once(self): attr = {"key1": "value1", "key2": "value2"} - limits = LogLimits( + limits = LogRecordLimits( max_attributes=1, max_attribute_length=1, ) @@ -167,17 +167,19 @@ def test_log_record_dropped_attributes_set_limits_warning_once(self): limits=limits, ) - # Check that at least one LogDroppedAttributesWarning was emitted + # Check that at least one LogRecordDroppedAttributesWarning was emitted dropped_attributes_warnings = [ - w for w in cw if isinstance(w.message, LogDroppedAttributesWarning) + w + for w in cw + if isinstance(w.message, LogRecordDroppedAttributesWarning) ] self.assertEqual( len(dropped_attributes_warnings), 1, - "Expected exactly one LogDroppedAttributesWarning due to simplefilter('once')", + "Expected exactly one LogRecordDroppedAttributesWarning due to simplefilter('once')", ) - # Check the message content of the LogDroppedAttributesWarning + # Check the message content of the LogRecordDroppedAttributesWarning warning_message = str(dropped_attributes_warnings[0].message) self.assertIn( "Log record attributes were dropped due to limits", @@ -186,7 +188,7 @@ def test_log_record_dropped_attributes_set_limits_warning_once(self): def test_log_record_dropped_attributes_unset_limits(self): attr = {"key": "value", "key2": "value2"} - limits = LogLimits() + limits = LogRecordLimits() result = ReadWriteLogRecord( LogRecord( diff --git a/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py b/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py index 3583148b41a..83f1bbb14a7 100644 --- a/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py +++ b/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py @@ -3,14 +3,14 @@ from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler from opentelemetry.sdk._logs.export import ( - InMemoryLogExporter, + InMemoryLogRecordExporter, SimpleLogRecordProcessor, ) def set_up_logging_handler(level): logger_provider = LoggerProvider() - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() processor = SimpleLogRecordProcessor(exporter=exporter) logger_provider.add_log_record_processor(processor) handler = LoggingHandler(level=level, logger_provider=logger_provider) diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 7f6c8190fe5..ec42cda6f6b 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -46,8 +46,8 @@ _OTelSDKConfigurator, ) from opentelemetry.sdk._logs import LoggingHandler -from opentelemetry.sdk._logs._internal.export import LogExporter -from opentelemetry.sdk._logs.export import ConsoleLogExporter +from opentelemetry.sdk._logs._internal.export import LogRecordExporter +from opentelemetry.sdk._logs.export import ConsoleLogRecordExporter from opentelemetry.sdk.environment_variables import ( OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG, @@ -221,7 +221,7 @@ def __init__(self, compression: str | None = None, *args, **kwargs): self.compression = compression -class DummyOTLPLogExporter(LogExporter): +class DummyOTLPLogExporter(LogRecordExporter): def __init__(self, compression: str | None = None, *args, **kwargs): self.export_called = False self.compression = compression @@ -1159,7 +1159,8 @@ def test_console_exporters(self): trace_exporters["console"].__class__, ConsoleSpanExporter.__class__ ) self.assertEqual( - logs_exporters["console"].__class__, ConsoleLogExporter.__class__ + logs_exporters["console"].__class__, + ConsoleLogRecordExporter.__class__, ) self.assertEqual( metric_exporterts["console"].__class__,