From 89341e9fde146a87796a69cc1a8b3a92be6911fb Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 26 Jun 2026 15:00:56 -0600 Subject: [PATCH 1/5] feat(config): wire top-level attribute_limits into per-signal providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parses config.attribute_limits in configure_sdk() and passes it as a global fallback to create_tracer_provider() and create_logger_provider(). Per-signal limits (tracer_provider.limits / logger_provider.limits) always take precedence; absent fields fall back to the global value, then to OTel spec defaults. For logs, adds log_record_limits to the LoggerProvider constructor, threads it through Logger, and applies it when constructing each ReadWriteLogRecord — mirroring how SpanLimits flows through TracerProvider. --- .changelog/5365.added | 1 + .../configuration/_logger_provider.py | 72 +++++++++++++++-- .../src/opentelemetry/configuration/_sdk.py | 10 ++- .../configuration/_tracer_provider.py | 63 +++++++++++---- .../tests/test_logger_provider.py | 77 +++++++++++++++---- opentelemetry-configuration/tests/test_sdk.py | 6 +- .../tests/test_tracer_provider.py | 70 +++++++++++++++++ .../sdk/_logs/_internal/__init__.py | 16 +++- 8 files changed, 272 insertions(+), 43 deletions(-) create mode 100644 .changelog/5365.added diff --git a/.changelog/5365.added b/.changelog/5365.added new file mode 100644 index 00000000000..c522a006681 --- /dev/null +++ b/.changelog/5365.added @@ -0,0 +1 @@ +`opentelemetry-sdk`: wire top-level `attribute_limits` into per-signal providers via declarative config; add `log_record_limits` support to `LoggerProvider` \ No newline at end of file diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py b/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py index 4214b2f9b76..7b36ce848dd 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py @@ -16,6 +16,9 @@ ConfigurationError, MissingDependencyError, ) +from opentelemetry.configuration.models import ( + AttributeLimits, +) from opentelemetry.configuration.models import ( BatchLogRecordProcessor as BatchLogRecordProcessorConfig, ) @@ -28,6 +31,9 @@ from opentelemetry.configuration.models import ( LogRecordExporter as LogRecordExporterConfig, ) +from opentelemetry.configuration.models import ( + LogRecordLimits as LogRecordLimitsConfig, +) from opentelemetry.configuration.models import ( LogRecordProcessor as LogRecordProcessorConfig, ) @@ -41,6 +47,7 @@ SimpleLogRecordProcessor as SimpleLogRecordProcessorConfig, ) from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk._logs._internal import LogRecordLimits from opentelemetry.sdk._logs._internal.export import ( BatchLogRecordProcessor, ConsoleLogRecordExporter, @@ -51,6 +58,8 @@ _logger = logging.getLogger(__name__) +_DEFAULT_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT = 128 + # BatchLogRecordProcessor defaults per OTel spec (milliseconds). _DEFAULT_SCHEDULE_DELAY_MILLIS = 1000 _DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000 @@ -216,9 +225,44 @@ def _create_log_record_processor( ) +def _create_log_record_limits( + config: LogRecordLimitsConfig, + global_limits: AttributeLimits | None = None, +) -> LogRecordLimits: + """Create LogRecordLimits from config. + + Absent fields fall back to global_limits (if provided), then to OTel spec + defaults (128 for counts, unlimited for lengths). + Explicit values suppress env-var reading — matching Java SDK behavior. + """ + attribute_count_limit = config.attribute_count_limit + if attribute_count_limit is None and global_limits is not None: + attribute_count_limit = global_limits.attribute_count_limit + + attribute_value_length_limit = config.attribute_value_length_limit + if attribute_value_length_limit is None and global_limits is not None: + attribute_value_length_limit = ( + global_limits.attribute_value_length_limit + ) + + return LogRecordLimits( + max_attributes=( + attribute_count_limit + if attribute_count_limit is not None + else _DEFAULT_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT + ), + max_attribute_length=( + attribute_value_length_limit + if attribute_value_length_limit is not None + else LogRecordLimits.UNSET + ), + ) + + def create_logger_provider( config: LoggerProviderConfig | None, resource: Resource | None = None, + global_attribute_limits: AttributeLimits | None = None, ) -> LoggerProvider: """Create an SDK LoggerProvider from declarative config. @@ -228,21 +272,28 @@ def create_logger_provider( Args: config: LoggerProvider config from the parsed config file, or None. resource: Resource to attach to the provider. + global_attribute_limits: Top-level attribute_limits from the root config, + used as a fallback when per-signal limits are not specified. Returns: A configured LoggerProvider. """ - provider = LoggerProvider(resource=resource) + if config is not None and config.limits is not None: + log_record_limits = _create_log_record_limits( + config.limits, global_attribute_limits + ) + else: + log_record_limits = _create_log_record_limits( + LogRecordLimitsConfig(), global_attribute_limits + ) + + provider = LoggerProvider( + resource=resource, log_record_limits=log_record_limits + ) if config is None: return provider - if config.limits is not None: - _logger.warning( - "log_record_limits are specified in config but are not supported " - "by the Python SDK LoggerProvider constructor; limits will be ignored." - ) - for processor_config in config.processors: provider.add_log_record_processor(_create_log_record_processor(processor_config)) @@ -252,6 +303,7 @@ def create_logger_provider( def configure_logger_provider( config: LoggerProviderConfig | None, resource: Resource | None = None, + global_attribute_limits: AttributeLimits | None = None, ) -> None: """Configure the global LoggerProvider from declarative config. @@ -261,7 +313,11 @@ def configure_logger_provider( Args: config: LoggerProvider config from the parsed config file, or None. resource: Resource to attach to the provider. + global_attribute_limits: Top-level attribute_limits from the root config, + used as a fallback when per-signal limits are not specified. """ if config is None: return - set_logger_provider(create_logger_provider(config, resource)) + set_logger_provider( + create_logger_provider(config, resource, global_attribute_limits) + ) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py b/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py index 698c88cce5d..f9347c33314 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py @@ -27,6 +27,7 @@ configure_instrumentation, ) from opentelemetry.configuration.models import ( + AttributeLimits, OpenTelemetryConfiguration, SeverityNumber, ) @@ -99,9 +100,14 @@ def configure_sdk(config: OpenTelemetryConfiguration) -> None: level = _SEVERITY_TO_LOGGING_LEVEL.get(config.log_level, INFO) getLogger("opentelemetry").setLevel(level) + global_attribute_limits: AttributeLimits | None = config.attribute_limits resource = create_resource(config.resource) - configure_tracer_provider(config.tracer_provider, resource) + configure_tracer_provider( + config.tracer_provider, resource, global_attribute_limits + ) configure_meter_provider(config.meter_provider, resource) - configure_logger_provider(config.logger_provider, resource) + configure_logger_provider( + config.logger_provider, resource, global_attribute_limits + ) configure_propagator(config.propagator) configure_instrumentation(config.instrumentation_development) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py b/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py index 733691dc67d..133a2d41b43 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py @@ -16,6 +16,9 @@ ConfigurationError, MissingDependencyError, ) +from opentelemetry.configuration.models import ( + AttributeLimits, +) from opentelemetry.configuration.models import ( ExperimentalComposableRuleBasedSampler as RuleBasedSamplerConfig, ) @@ -357,16 +360,30 @@ def _create_parent_based_sampler(config: ParentBasedSamplerConfig) -> Sampler: return ParentBased(**kwargs) -def _create_span_limits(config: SpanLimitsConfig) -> SpanLimits: +def _create_span_limits( + config: SpanLimitsConfig, + global_limits: AttributeLimits | None = None, +) -> SpanLimits: """Create SpanLimits from config. - Absent fields use the OTel spec defaults (128 for counts, unlimited for lengths). + Absent fields fall back to global_limits (if provided), then to OTel spec + defaults (128 for counts, unlimited for lengths). Explicit values suppress env-var reading — matching Java SDK behavior. """ + attribute_count_limit = config.attribute_count_limit + if attribute_count_limit is None and global_limits is not None: + attribute_count_limit = global_limits.attribute_count_limit + + attribute_value_length_limit = config.attribute_value_length_limit + if attribute_value_length_limit is None and global_limits is not None: + attribute_value_length_limit = ( + global_limits.attribute_value_length_limit + ) + return SpanLimits( max_span_attributes=( - config.attribute_count_limit - if config.attribute_count_limit is not None + attribute_count_limit + if attribute_count_limit is not None else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT ), max_events=( @@ -385,13 +402,23 @@ def _create_span_limits(config: SpanLimitsConfig) -> SpanLimits: if config.link_attribute_count_limit is not None else _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT ), - max_attribute_length=config.attribute_value_length_limit, + max_attribute_length=( + attribute_value_length_limit + if attribute_value_length_limit is not None + else SpanLimits.UNSET + ), + max_span_attribute_length=( + attribute_value_length_limit + if attribute_value_length_limit is not None + else SpanLimits.UNSET + ), ) def create_tracer_provider( config: TracerProviderConfig | None, resource: Resource | None = None, + global_attribute_limits: AttributeLimits | None = None, ) -> TracerProvider: """Create an SDK TracerProvider from declarative config. @@ -402,6 +429,8 @@ def create_tracer_provider( Args: config: TracerProvider config from the parsed config file, or None. resource: Resource to attach to the provider. + global_attribute_limits: Top-level attribute_limits from the root config, + used as a fallback when per-signal limits are not specified. Returns: A configured TracerProvider. @@ -410,17 +439,14 @@ def create_tracer_provider( id_generator = ( _create_id_generator(config.id_generator) if config is not None and config.id_generator is not None else None ) - span_limits = ( - _create_span_limits(config.limits) - if config is not None and config.limits is not None - else SpanLimits( - max_span_attributes=_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, - max_events=_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT, - max_links=_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT, - max_event_attributes=_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, - max_link_attributes=_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + if config is not None and config.limits is not None: + span_limits = _create_span_limits( + config.limits, global_attribute_limits + ) + else: + span_limits = _create_span_limits( + SpanLimitsConfig(), global_attribute_limits ) - ) provider = TracerProvider( resource=resource, @@ -439,6 +465,7 @@ def create_tracer_provider( def configure_tracer_provider( config: TracerProviderConfig | None, resource: Resource | None = None, + global_attribute_limits: AttributeLimits | None = None, ) -> None: """Configure the global TracerProvider from declarative config. @@ -449,7 +476,11 @@ def configure_tracer_provider( Args: config: TracerProvider config from the parsed config file, or None. resource: Resource to attach to the provider. + global_attribute_limits: Top-level attribute_limits from the root config, + used as a fallback when per-signal limits are not specified. """ if config is None: return - trace.set_tracer_provider(create_tracer_provider(config, resource)) + trace.set_tracer_provider( + create_tracer_provider(config, resource, global_attribute_limits) + ) diff --git a/opentelemetry-configuration/tests/test_logger_provider.py b/opentelemetry-configuration/tests/test_logger_provider.py index 9965e4b6bfe..a022a0db99b 100644 --- a/opentelemetry-configuration/tests/test_logger_provider.py +++ b/opentelemetry-configuration/tests/test_logger_provider.py @@ -4,6 +4,7 @@ # Tests access private members of SDK classes to assert correct configuration. # pylint: disable=protected-access +import os import sys import unittest from unittest.mock import MagicMock, patch @@ -22,6 +23,10 @@ create_logger_provider, ) from opentelemetry.configuration.file._loader import ConfigurationError +from opentelemetry.configuration.models import ( + AttributeLimits, + NameStringValuePair, +) from opentelemetry.configuration.models import ( BatchLogRecordProcessor as BatchLogRecordProcessorConfig, ) @@ -40,9 +45,6 @@ from opentelemetry.configuration.models import ( LogRecordProcessor as LogRecordProcessorConfig, ) -from opentelemetry.configuration.models import ( - NameStringValuePair, -) from opentelemetry.configuration.models import ( OtlpGrpcExporter as OtlpGrpcExporterConfig, ) @@ -409,20 +411,34 @@ def test_otlp_grpc_exporter_endpoint(self): class TestLogRecordLimits(unittest.TestCase): - def test_limits_logs_warning(self): + def test_default_limits(self): + provider = create_logger_provider(None) + self.assertEqual(provider._log_record_limits.max_attributes, 128) + self.assertIsNone(provider._log_record_limits.max_attribute_length) + + def test_default_limits_do_not_read_env_vars(self): + with patch.dict( + os.environ, + { + "OTEL_ATTRIBUTE_COUNT_LIMIT": "1", + "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT": "2", + }, + ): + provider = create_logger_provider(None) + self.assertEqual(provider._log_record_limits.max_attributes, 128) + self.assertIsNone(provider._log_record_limits.max_attribute_length) + + def test_limits_from_config(self): config = LoggerProviderConfig( processors=[], - limits=LogRecordLimitsConfig(attribute_count_limit=64), - ) - with self.assertLogs( - "opentelemetry.configuration._logger_provider", - level="WARNING", - ) as cm: - create_logger_provider(config) - self.assertTrue( - any("limits" in msg for msg in cm.output), - "Expected warning about unsupported limits", + limits=LogRecordLimitsConfig( + attribute_count_limit=64, + attribute_value_length_limit=256, + ), ) + provider = create_logger_provider(config) + self.assertEqual(provider._log_record_limits.max_attributes, 64) + self.assertEqual(provider._log_record_limits.max_attribute_length, 256) @staticmethod def test_no_limits_no_warning(): @@ -431,6 +447,39 @@ def test_no_limits_no_warning(): create_logger_provider(config) mock_logger.warning.assert_not_called() + def test_global_attribute_count_limit_used_when_no_per_signal_limits(self): + global_limits = AttributeLimits(attribute_count_limit=42) + provider = create_logger_provider( + None, global_attribute_limits=global_limits + ) + self.assertEqual(provider._log_record_limits.max_attributes, 42) + + def test_global_attribute_value_length_limit_used_when_no_per_signal_limits( + self, + ): + global_limits = AttributeLimits(attribute_value_length_limit=64) + provider = create_logger_provider( + None, global_attribute_limits=global_limits + ) + self.assertEqual(provider._log_record_limits.max_attribute_length, 64) + + def test_per_signal_limits_override_global(self): + global_limits = AttributeLimits( + attribute_count_limit=100, attribute_value_length_limit=200 + ) + config = LoggerProviderConfig( + processors=[], + limits=LogRecordLimitsConfig( + attribute_count_limit=7, + attribute_value_length_limit=16, + ), + ) + provider = create_logger_provider( + config, global_attribute_limits=global_limits + ) + self.assertEqual(provider._log_record_limits.max_attributes, 7) + self.assertEqual(provider._log_record_limits.max_attribute_length, 16) + if __name__ == "__main__": unittest.main() diff --git a/opentelemetry-configuration/tests/test_sdk.py b/opentelemetry-configuration/tests/test_sdk.py index 6a6d2cf1be6..5f5ccecc0df 100644 --- a/opentelemetry-configuration/tests/test_sdk.py +++ b/opentelemetry-configuration/tests/test_sdk.py @@ -70,9 +70,11 @@ def test_calls_each_signal_with_resource( configure_sdk(config) mock_create_resource.assert_called_once_with(resource_cfg) - mock_tracer.assert_called_once_with(tracer_cfg, sentinel_resource) + mock_tracer.assert_called_once_with( + tracer_cfg, sentinel_resource, None + ) mock_meter.assert_called_once_with(None, sentinel_resource) - mock_logger.assert_called_once_with(None, sentinel_resource) + mock_logger.assert_called_once_with(None, sentinel_resource, None) mock_propagator.assert_called_once_with(propagator_cfg) @patch("opentelemetry.configuration._sdk.configure_propagator") diff --git a/opentelemetry-configuration/tests/test_tracer_provider.py b/opentelemetry-configuration/tests/test_tracer_provider.py index 9c950a0e049..266eaf0f0ad 100644 --- a/opentelemetry-configuration/tests/test_tracer_provider.py +++ b/opentelemetry-configuration/tests/test_tracer_provider.py @@ -16,6 +16,9 @@ create_tracer_provider, ) from opentelemetry.configuration.file._loader import ConfigurationError +from opentelemetry.configuration.models import ( + AttributeLimits, +) from opentelemetry.configuration.models import ( BatchSpanProcessor as BatchSpanProcessorConfig, ) @@ -780,11 +783,15 @@ def test_absent_limits_do_not_read_env_vars(self): { "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT": "1", "OTEL_SPAN_EVENT_COUNT_LIMIT": "2", + "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT": "3", + "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT": "4", }, ): provider = self._create_with_limits(SpanLimitsConfig()) self.assertEqual(provider._span_limits.max_span_attributes, 128) self.assertEqual(provider._span_limits.max_events, 128) + self.assertIsNone(provider._span_limits.max_attribute_length) + self.assertIsNone(provider._span_limits.max_span_attribute_length) class TestCreateIdGenerator(unittest.TestCase): @@ -830,3 +837,66 @@ def test_empty_id_generator_raises_configuration_error(self): """Empty IdGenerator config (no type specified) raises ConfigurationError.""" with self.assertRaises(ConfigurationError): self._make_provider(IdGeneratorConfig()) + + +class TestGlobalAttributeLimitsFallback(unittest.TestCase): + # pylint: disable=no-self-use + + def test_global_attribute_count_limit_used_when_no_per_signal_limits(self): + global_limits = AttributeLimits(attribute_count_limit=42) + provider = create_tracer_provider( + TracerProviderConfig(processors=[]), + global_attribute_limits=global_limits, + ) + self.assertEqual(provider._span_limits.max_span_attributes, 42) + + def test_global_attribute_value_length_limit_used_when_no_per_signal_limits( + self, + ): + global_limits = AttributeLimits(attribute_value_length_limit=64) + provider = create_tracer_provider( + TracerProviderConfig(processors=[]), + global_attribute_limits=global_limits, + ) + self.assertEqual(provider._span_limits.max_attribute_length, 64) + + def test_per_signal_limits_take_precedence_over_global(self): + global_limits = AttributeLimits( + attribute_count_limit=99, + attribute_value_length_limit=99, + ) + provider = create_tracer_provider( + TracerProviderConfig( + processors=[], + limits=SpanLimitsConfig( + attribute_count_limit=7, + attribute_value_length_limit=16, + ), + ), + global_attribute_limits=global_limits, + ) + self.assertEqual(provider._span_limits.max_span_attributes, 7) + self.assertEqual(provider._span_limits.max_attribute_length, 16) + + def test_global_limits_absent_uses_sdk_defaults(self): + provider = create_tracer_provider( + TracerProviderConfig(processors=[]), + ) + self.assertEqual(provider._span_limits.max_span_attributes, 128) + self.assertIsNone(provider._span_limits.max_attribute_length) + + def test_global_limits_absent_does_not_read_env_vars(self): + with patch.dict( + os.environ, + { + "OTEL_ATTRIBUTE_COUNT_LIMIT": "1", + "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT": "2", + "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT": "3", + }, + ): + provider = create_tracer_provider( + TracerProviderConfig(processors=[]), + ) + self.assertEqual(provider._span_limits.max_span_attributes, 128) + self.assertIsNone(provider._span_limits.max_attribute_length) + self.assertIsNone(provider._span_limits.max_span_attribute_length) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py index 300f5c3cecc..077044cc968 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py @@ -123,7 +123,7 @@ class LogRecordLimits: This class does not enforce any limits itself. It only provides a way to read limits from env, default values and from user provided arguments. - All limit arguments must be either a non-negative integer or ``None``. + All limit arguments must be either a non-negative integer, ``None`` or ``LogRecordLimits.UNSET``. - All limit arguments are optional. - If a limit argument is not set, the class will try to read its value from the corresponding @@ -151,6 +151,8 @@ class LogRecordLimits: Falls back to ``max_attribute_length`` when unset. """ + UNSET = -1 + def __init__( self, max_attributes: int | None = None, @@ -193,6 +195,9 @@ def __repr__(self): @classmethod def _from_env_if_absent(cls, value: int | None, env_var: str, default: int | None = None) -> int | None: + if value == cls.UNSET: + return None + err_msg = "{} must be a non-negative integer but got {}" # if no value is provided for the limit, try to load it from env @@ -310,11 +315,13 @@ def _from_api_log_record( record: LogRecord, resource: Resource, instrumentation_scope: InstrumentationScope | None = None, + limits: LogRecordLimits | None = None, ) -> ReadWriteLogRecord: return cls( log_record=record, resource=resource, instrumentation_scope=instrumentation_scope, + **({} if limits is None else {"limits": limits}), ) @@ -682,6 +689,7 @@ def __init__( instrumentation_scope: InstrumentationScope, *, logger_metrics: LoggerMetricsT, + log_record_limits: LogRecordLimits | None = None, _logger_config: _LoggerConfig, ): super().__init__( @@ -695,6 +703,7 @@ def __init__( self._instrumentation_scope = instrumentation_scope self._logger_metrics = logger_metrics self._logger_config = _logger_config + self._log_record_limits = log_record_limits or LogRecordLimits() def _is_enabled(self) -> bool: return self._logger_config.is_enabled @@ -743,6 +752,7 @@ def emit( record=record, resource=self._resource, instrumentation_scope=self._instrumentation_scope, + limits=self._log_record_limits, ) else: _set_log_record_exception_attributes(record.log_record) @@ -765,6 +775,7 @@ def emit( record=log_record, resource=self._resource, instrumentation_scope=self._instrumentation_scope, + limits=self._log_record_limits, ) self._logger_metrics.emit_log() @@ -797,6 +808,7 @@ def __init__( | None = None, *, meter_provider: MeterProvider | None = None, + log_record_limits: LogRecordLimits | None = None, _logger_configurator: _LoggerConfiguratorT | None = None, ): if resource is None: @@ -811,6 +823,7 @@ def __init__( disabled = environ.get(OTEL_SDK_DISABLED, "") self._disabled = disabled.lower().strip() == "true" self._logger_configurator = _logger_configurator or _default_logger_configurator + self._log_record_limits = log_record_limits or LogRecordLimits() self._at_exit_handler = None if shutdown_on_exit: self._at_exit_handler = atexit.register(self.shutdown) @@ -858,6 +871,7 @@ def _get_logger_no_cache( scope, logger_metrics=self._logger_metrics, _logger_config=self._apply_logger_configurator(scope), + log_record_limits=self._log_record_limits, ) def _get_logger_cached( From 227d879a2f732245bd4355a2fb774f9550eec824 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 7 Aug 2026 10:09:59 -0500 Subject: [PATCH 2/5] Fix limits --- .../configuration/_logger_provider.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py b/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py index 7b36ce848dd..6811985136b 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py @@ -279,13 +279,16 @@ def create_logger_provider( A configured LoggerProvider. """ if config is not None and config.limits is not None: - log_record_limits = _create_log_record_limits( - config.limits, global_attribute_limits - ) + + limits = config.limits + else: - log_record_limits = _create_log_record_limits( - LogRecordLimitsConfig(), global_attribute_limits - ) + + limits = LogRecordLimitsConfig() + + log_record_limits = _create_log_record_limits( + limits, global_attribute_limits + ) provider = LoggerProvider( resource=resource, log_record_limits=log_record_limits From 0f9e3898a7af28552a556954ea4ba03e5de11411 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 7 Aug 2026 10:11:39 -0500 Subject: [PATCH 3/5] Fix limits --- .../configuration/_tracer_provider.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py b/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py index 133a2d41b43..f5d6a7259c2 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py @@ -440,13 +440,16 @@ def create_tracer_provider( _create_id_generator(config.id_generator) if config is not None and config.id_generator is not None else None ) if config is not None and config.limits is not None: - span_limits = _create_span_limits( - config.limits, global_attribute_limits - ) + + limits = config.limits + else: - span_limits = _create_span_limits( - SpanLimitsConfig(), global_attribute_limits - ) + + limits = SpanLimitsConfig() + + span_limits = _create_span_limits( + limits, global_attribute_limits + ) provider = TracerProvider( resource=resource, From cef2d13649a91051f883e8bffc2e7085809567ad Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 7 Aug 2026 10:12:10 -0500 Subject: [PATCH 4/5] Update opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py Co-authored-by: Aaron Abbott --- .../src/opentelemetry/configuration/_sdk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py b/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py index f9347c33314..373e8ba629a 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py @@ -100,7 +100,7 @@ def configure_sdk(config: OpenTelemetryConfiguration) -> None: level = _SEVERITY_TO_LOGGING_LEVEL.get(config.log_level, INFO) getLogger("opentelemetry").setLevel(level) - global_attribute_limits: AttributeLimits | None = config.attribute_limits + global_attribute_limits = config.attribute_limits resource = create_resource(config.resource) configure_tracer_provider( config.tracer_provider, resource, global_attribute_limits From 252abd5b37aaa3412314fa6fbc5b547ee2e02751 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 7 Aug 2026 10:48:17 -0500 Subject: [PATCH 5/5] Fix lint: remove unused import and apply ruff format --- .../configuration/_logger_provider.py | 26 +++++-------------- .../src/opentelemetry/configuration/_sdk.py | 9 ++----- .../configuration/_tracer_provider.py | 26 +++++-------------- .../tests/test_logger_provider.py | 16 +++--------- opentelemetry-configuration/tests/test_sdk.py | 4 +-- 5 files changed, 19 insertions(+), 62 deletions(-) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py b/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py index 6811985136b..92fe801a526 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py @@ -241,20 +241,14 @@ def _create_log_record_limits( attribute_value_length_limit = config.attribute_value_length_limit if attribute_value_length_limit is None and global_limits is not None: - attribute_value_length_limit = ( - global_limits.attribute_value_length_limit - ) + attribute_value_length_limit = global_limits.attribute_value_length_limit return LogRecordLimits( max_attributes=( - attribute_count_limit - if attribute_count_limit is not None - else _DEFAULT_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit if attribute_count_limit is not None else _DEFAULT_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT ), max_attribute_length=( - attribute_value_length_limit - if attribute_value_length_limit is not None - else LogRecordLimits.UNSET + attribute_value_length_limit if attribute_value_length_limit is not None else LogRecordLimits.UNSET ), ) @@ -279,20 +273,14 @@ def create_logger_provider( A configured LoggerProvider. """ if config is not None and config.limits is not None: - limits = config.limits else: - limits = LogRecordLimitsConfig() - log_record_limits = _create_log_record_limits( - limits, global_attribute_limits - ) + log_record_limits = _create_log_record_limits(limits, global_attribute_limits) - provider = LoggerProvider( - resource=resource, log_record_limits=log_record_limits - ) + provider = LoggerProvider(resource=resource, log_record_limits=log_record_limits) if config is None: return provider @@ -321,6 +309,4 @@ def configure_logger_provider( """ if config is None: return - set_logger_provider( - create_logger_provider(config, resource, global_attribute_limits) - ) + set_logger_provider(create_logger_provider(config, resource, global_attribute_limits)) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py b/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py index 373e8ba629a..12b5081b834 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py @@ -27,7 +27,6 @@ configure_instrumentation, ) from opentelemetry.configuration.models import ( - AttributeLimits, OpenTelemetryConfiguration, SeverityNumber, ) @@ -102,12 +101,8 @@ def configure_sdk(config: OpenTelemetryConfiguration) -> None: global_attribute_limits = config.attribute_limits resource = create_resource(config.resource) - configure_tracer_provider( - config.tracer_provider, resource, global_attribute_limits - ) + configure_tracer_provider(config.tracer_provider, resource, global_attribute_limits) configure_meter_provider(config.meter_provider, resource) - configure_logger_provider( - config.logger_provider, resource, global_attribute_limits - ) + configure_logger_provider(config.logger_provider, resource, global_attribute_limits) configure_propagator(config.propagator) configure_instrumentation(config.instrumentation_development) diff --git a/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py b/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py index f5d6a7259c2..53a69c3359b 100644 --- a/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py +++ b/opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py @@ -376,15 +376,11 @@ def _create_span_limits( attribute_value_length_limit = config.attribute_value_length_limit if attribute_value_length_limit is None and global_limits is not None: - attribute_value_length_limit = ( - global_limits.attribute_value_length_limit - ) + attribute_value_length_limit = global_limits.attribute_value_length_limit return SpanLimits( max_span_attributes=( - attribute_count_limit - if attribute_count_limit is not None - else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit if attribute_count_limit is not None else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT ), max_events=( config.event_count_limit if config.event_count_limit is not None else _DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT @@ -403,14 +399,10 @@ def _create_span_limits( else _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT ), max_attribute_length=( - attribute_value_length_limit - if attribute_value_length_limit is not None - else SpanLimits.UNSET + attribute_value_length_limit if attribute_value_length_limit is not None else SpanLimits.UNSET ), max_span_attribute_length=( - attribute_value_length_limit - if attribute_value_length_limit is not None - else SpanLimits.UNSET + attribute_value_length_limit if attribute_value_length_limit is not None else SpanLimits.UNSET ), ) @@ -440,16 +432,12 @@ def create_tracer_provider( _create_id_generator(config.id_generator) if config is not None and config.id_generator is not None else None ) if config is not None and config.limits is not None: - limits = config.limits else: - limits = SpanLimitsConfig() - span_limits = _create_span_limits( - limits, global_attribute_limits - ) + span_limits = _create_span_limits(limits, global_attribute_limits) provider = TracerProvider( resource=resource, @@ -484,6 +472,4 @@ def configure_tracer_provider( """ if config is None: return - trace.set_tracer_provider( - create_tracer_provider(config, resource, global_attribute_limits) - ) + trace.set_tracer_provider(create_tracer_provider(config, resource, global_attribute_limits)) diff --git a/opentelemetry-configuration/tests/test_logger_provider.py b/opentelemetry-configuration/tests/test_logger_provider.py index a022a0db99b..306e3e76ffa 100644 --- a/opentelemetry-configuration/tests/test_logger_provider.py +++ b/opentelemetry-configuration/tests/test_logger_provider.py @@ -449,24 +449,18 @@ def test_no_limits_no_warning(): def test_global_attribute_count_limit_used_when_no_per_signal_limits(self): global_limits = AttributeLimits(attribute_count_limit=42) - provider = create_logger_provider( - None, global_attribute_limits=global_limits - ) + provider = create_logger_provider(None, global_attribute_limits=global_limits) self.assertEqual(provider._log_record_limits.max_attributes, 42) def test_global_attribute_value_length_limit_used_when_no_per_signal_limits( self, ): global_limits = AttributeLimits(attribute_value_length_limit=64) - provider = create_logger_provider( - None, global_attribute_limits=global_limits - ) + provider = create_logger_provider(None, global_attribute_limits=global_limits) self.assertEqual(provider._log_record_limits.max_attribute_length, 64) def test_per_signal_limits_override_global(self): - global_limits = AttributeLimits( - attribute_count_limit=100, attribute_value_length_limit=200 - ) + global_limits = AttributeLimits(attribute_count_limit=100, attribute_value_length_limit=200) config = LoggerProviderConfig( processors=[], limits=LogRecordLimitsConfig( @@ -474,9 +468,7 @@ def test_per_signal_limits_override_global(self): attribute_value_length_limit=16, ), ) - provider = create_logger_provider( - config, global_attribute_limits=global_limits - ) + provider = create_logger_provider(config, global_attribute_limits=global_limits) self.assertEqual(provider._log_record_limits.max_attributes, 7) self.assertEqual(provider._log_record_limits.max_attribute_length, 16) diff --git a/opentelemetry-configuration/tests/test_sdk.py b/opentelemetry-configuration/tests/test_sdk.py index 5f5ccecc0df..f2a1fc6ab69 100644 --- a/opentelemetry-configuration/tests/test_sdk.py +++ b/opentelemetry-configuration/tests/test_sdk.py @@ -70,9 +70,7 @@ def test_calls_each_signal_with_resource( configure_sdk(config) mock_create_resource.assert_called_once_with(resource_cfg) - mock_tracer.assert_called_once_with( - tracer_cfg, sentinel_resource, None - ) + mock_tracer.assert_called_once_with(tracer_cfg, sentinel_resource, None) mock_meter.assert_called_once_with(None, sentinel_resource) mock_logger.assert_called_once_with(None, sentinel_resource, None) mock_propagator.assert_called_once_with(propagator_cfg)