Skip to content

Commit 9f37481

Browse files
committed
feat(config): wire top-level attribute_limits into per-signal providers
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.
1 parent d10f472 commit 9f37481

8 files changed

Lines changed: 273 additions & 51 deletions

File tree

.changelog/5365.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`opentelemetry-sdk`: wire top-level `attribute_limits` into per-signal providers via declarative config; add `log_record_limits` support to `LoggerProvider`

opentelemetry-configuration/src/opentelemetry/configuration/_logger_provider.py

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
ConfigurationError,
1717
MissingDependencyError,
1818
)
19+
from opentelemetry.configuration.models import (
20+
AttributeLimits,
21+
)
1922
from opentelemetry.configuration.models import (
2023
BatchLogRecordProcessor as BatchLogRecordProcessorConfig,
2124
)
@@ -28,6 +31,9 @@
2831
from opentelemetry.configuration.models import (
2932
LogRecordExporter as LogRecordExporterConfig,
3033
)
34+
from opentelemetry.configuration.models import (
35+
LogRecordLimits as LogRecordLimitsConfig,
36+
)
3137
from opentelemetry.configuration.models import (
3238
LogRecordProcessor as LogRecordProcessorConfig,
3339
)
@@ -41,6 +47,7 @@
4147
SimpleLogRecordProcessor as SimpleLogRecordProcessorConfig,
4248
)
4349
from opentelemetry.sdk._logs import LoggerProvider
50+
from opentelemetry.sdk._logs._internal import LogRecordLimits
4451
from opentelemetry.sdk._logs._internal.export import (
4552
BatchLogRecordProcessor,
4653
ConsoleLogRecordExporter,
@@ -51,6 +58,8 @@
5158

5259
_logger = logging.getLogger(__name__)
5360

61+
_DEFAULT_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT = 128
62+
5463
# BatchLogRecordProcessor defaults per OTel spec (milliseconds).
5564
_DEFAULT_SCHEDULE_DELAY_MILLIS = 1000
5665
_DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000
@@ -235,9 +244,44 @@ def _create_log_record_processor(
235244
)
236245

237246

247+
def _create_log_record_limits(
248+
config: LogRecordLimitsConfig,
249+
global_limits: AttributeLimits | None = None,
250+
) -> LogRecordLimits:
251+
"""Create LogRecordLimits from config.
252+
253+
Absent fields fall back to global_limits (if provided), then to OTel spec
254+
defaults (128 for counts, unlimited for lengths).
255+
Explicit values suppress env-var reading — matching Java SDK behavior.
256+
"""
257+
attribute_count_limit = config.attribute_count_limit
258+
if attribute_count_limit is None and global_limits is not None:
259+
attribute_count_limit = global_limits.attribute_count_limit
260+
261+
attribute_value_length_limit = config.attribute_value_length_limit
262+
if attribute_value_length_limit is None and global_limits is not None:
263+
attribute_value_length_limit = (
264+
global_limits.attribute_value_length_limit
265+
)
266+
267+
return LogRecordLimits(
268+
max_attributes=(
269+
attribute_count_limit
270+
if attribute_count_limit is not None
271+
else _DEFAULT_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT
272+
),
273+
max_attribute_length=(
274+
attribute_value_length_limit
275+
if attribute_value_length_limit is not None
276+
else LogRecordLimits.UNSET
277+
),
278+
)
279+
280+
238281
def create_logger_provider(
239282
config: LoggerProviderConfig | None,
240283
resource: Resource | None = None,
284+
global_attribute_limits: AttributeLimits | None = None,
241285
) -> LoggerProvider:
242286
"""Create an SDK LoggerProvider from declarative config.
243287
@@ -247,21 +291,28 @@ def create_logger_provider(
247291
Args:
248292
config: LoggerProvider config from the parsed config file, or None.
249293
resource: Resource to attach to the provider.
294+
global_attribute_limits: Top-level attribute_limits from the root config,
295+
used as a fallback when per-signal limits are not specified.
250296
251297
Returns:
252298
A configured LoggerProvider.
253299
"""
254-
provider = LoggerProvider(resource=resource)
300+
if config is not None and config.limits is not None:
301+
log_record_limits = _create_log_record_limits(
302+
config.limits, global_attribute_limits
303+
)
304+
else:
305+
log_record_limits = _create_log_record_limits(
306+
LogRecordLimitsConfig(), global_attribute_limits
307+
)
308+
309+
provider = LoggerProvider(
310+
resource=resource, log_record_limits=log_record_limits
311+
)
255312

256313
if config is None:
257314
return provider
258315

259-
if config.limits is not None:
260-
_logger.warning(
261-
"log_record_limits are specified in config but are not supported "
262-
"by the Python SDK LoggerProvider constructor; limits will be ignored."
263-
)
264-
265316
for processor_config in config.processors:
266317
provider.add_log_record_processor(
267318
_create_log_record_processor(processor_config)
@@ -273,6 +324,7 @@ def create_logger_provider(
273324
def configure_logger_provider(
274325
config: LoggerProviderConfig | None,
275326
resource: Resource | None = None,
327+
global_attribute_limits: AttributeLimits | None = None,
276328
) -> None:
277329
"""Configure the global LoggerProvider from declarative config.
278330
@@ -282,7 +334,11 @@ def configure_logger_provider(
282334
Args:
283335
config: LoggerProvider config from the parsed config file, or None.
284336
resource: Resource to attach to the provider.
337+
global_attribute_limits: Top-level attribute_limits from the root config,
338+
used as a fallback when per-signal limits are not specified.
285339
"""
286340
if config is None:
287341
return
288-
set_logger_provider(create_logger_provider(config, resource))
342+
set_logger_provider(
343+
create_logger_provider(config, resource, global_attribute_limits)
344+
)

opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
from opentelemetry.configuration.instrumentation import (
2727
configure_instrumentation,
2828
)
29-
from opentelemetry.configuration.models import OpenTelemetryConfiguration
29+
from opentelemetry.configuration.models import (
30+
AttributeLimits,
31+
OpenTelemetryConfiguration,
32+
)
3033

3134
_logger = getLogger(__name__)
3235

@@ -60,9 +63,14 @@ def configure_sdk(config: OpenTelemetryConfiguration) -> None:
6063
)
6164
return
6265

66+
global_attribute_limits: AttributeLimits | None = config.attribute_limits
6367
resource = create_resource(config.resource)
64-
configure_tracer_provider(config.tracer_provider, resource)
68+
configure_tracer_provider(
69+
config.tracer_provider, resource, global_attribute_limits
70+
)
6571
configure_meter_provider(config.meter_provider, resource)
66-
configure_logger_provider(config.logger_provider, resource)
72+
configure_logger_provider(
73+
config.logger_provider, resource, global_attribute_limits
74+
)
6775
configure_propagator(config.propagator)
6876
configure_instrumentation(config.instrumentation_development)

opentelemetry-configuration/src/opentelemetry/configuration/_tracer_provider.py

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
ConfigurationError,
1717
MissingDependencyError,
1818
)
19+
from opentelemetry.configuration.models import (
20+
AttributeLimits,
21+
)
1922
from opentelemetry.configuration.models import (
2023
ExperimentalComposableRuleBasedSampler as RuleBasedSamplerConfig,
2124
)
@@ -400,16 +403,30 @@ def _create_parent_based_sampler(config: ParentBasedSamplerConfig) -> Sampler:
400403
return ParentBased(**kwargs)
401404

402405

403-
def _create_span_limits(config: SpanLimitsConfig) -> SpanLimits:
406+
def _create_span_limits(
407+
config: SpanLimitsConfig,
408+
global_limits: AttributeLimits | None = None,
409+
) -> SpanLimits:
404410
"""Create SpanLimits from config.
405411
406-
Absent fields use the OTel spec defaults (128 for counts, unlimited for lengths).
412+
Absent fields fall back to global_limits (if provided), then to OTel spec
413+
defaults (128 for counts, unlimited for lengths).
407414
Explicit values suppress env-var reading — matching Java SDK behavior.
408415
"""
416+
attribute_count_limit = config.attribute_count_limit
417+
if attribute_count_limit is None and global_limits is not None:
418+
attribute_count_limit = global_limits.attribute_count_limit
419+
420+
attribute_value_length_limit = config.attribute_value_length_limit
421+
if attribute_value_length_limit is None and global_limits is not None:
422+
attribute_value_length_limit = (
423+
global_limits.attribute_value_length_limit
424+
)
425+
409426
return SpanLimits(
410427
max_span_attributes=(
411-
config.attribute_count_limit
412-
if config.attribute_count_limit is not None
428+
attribute_count_limit
429+
if attribute_count_limit is not None
413430
else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
414431
),
415432
max_events=(
@@ -432,13 +449,23 @@ def _create_span_limits(config: SpanLimitsConfig) -> SpanLimits:
432449
if config.link_attribute_count_limit is not None
433450
else _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT
434451
),
435-
max_attribute_length=config.attribute_value_length_limit,
452+
max_attribute_length=(
453+
attribute_value_length_limit
454+
if attribute_value_length_limit is not None
455+
else SpanLimits.UNSET
456+
),
457+
max_span_attribute_length=(
458+
attribute_value_length_limit
459+
if attribute_value_length_limit is not None
460+
else SpanLimits.UNSET
461+
),
436462
)
437463

438464

439465
def create_tracer_provider(
440466
config: TracerProviderConfig | None,
441467
resource: Resource | None = None,
468+
global_attribute_limits: AttributeLimits | None = None,
442469
) -> TracerProvider:
443470
"""Create an SDK TracerProvider from declarative config.
444471
@@ -449,6 +476,8 @@ def create_tracer_provider(
449476
Args:
450477
config: TracerProvider config from the parsed config file, or None.
451478
resource: Resource to attach to the provider.
479+
global_attribute_limits: Top-level attribute_limits from the root config,
480+
used as a fallback when per-signal limits are not specified.
452481
453482
Returns:
454483
A configured TracerProvider.
@@ -463,17 +492,14 @@ def create_tracer_provider(
463492
if config is not None and config.id_generator is not None
464493
else None
465494
)
466-
span_limits = (
467-
_create_span_limits(config.limits)
468-
if config is not None and config.limits is not None
469-
else SpanLimits(
470-
max_span_attributes=_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
471-
max_events=_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT,
472-
max_links=_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT,
473-
max_event_attributes=_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
474-
max_link_attributes=_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
495+
if config is not None and config.limits is not None:
496+
span_limits = _create_span_limits(
497+
config.limits, global_attribute_limits
498+
)
499+
else:
500+
span_limits = _create_span_limits(
501+
SpanLimitsConfig(), global_attribute_limits
475502
)
476-
)
477503

478504
provider = TracerProvider(
479505
resource=resource,
@@ -492,6 +518,7 @@ def create_tracer_provider(
492518
def configure_tracer_provider(
493519
config: TracerProviderConfig | None,
494520
resource: Resource | None = None,
521+
global_attribute_limits: AttributeLimits | None = None,
495522
) -> None:
496523
"""Configure the global TracerProvider from declarative config.
497524
@@ -502,7 +529,11 @@ def configure_tracer_provider(
502529
Args:
503530
config: TracerProvider config from the parsed config file, or None.
504531
resource: Resource to attach to the provider.
532+
global_attribute_limits: Top-level attribute_limits from the root config,
533+
used as a fallback when per-signal limits are not specified.
505534
"""
506535
if config is None:
507536
return
508-
trace.set_tracer_provider(create_tracer_provider(config, resource))
537+
trace.set_tracer_provider(
538+
create_tracer_provider(config, resource, global_attribute_limits)
539+
)

0 commit comments

Comments
 (0)