Uh oh!
There was an error while loading. Please reload this page.
Python: Allow programmatic OTel service name, resource attributes, and OTLP exporter config - #7703
Conversation
…d OTLP exporter config in configure_otel_providers() Previously, configure_otel_providers() only read service.name, resource attributes, OTLP endpoint, protocol, headers, timeout, and compression from environment variables (OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_EXPORTER_OTLP_*), even though the lower-level create_resource() helper already supported passing these programmatically. This forced any caller of the documented one-call setup entry point to rely on env vars/.env files for basic telemetry identification and exporter configuration. Add service_name, service_version, resource_attributes, otlp_endpoint, otlp_protocol, otlp_headers, otlp_timeout, and otlp_compression keyword arguments to configure_otel_providers() and ObservabilitySettings, threaded through to create_resource() and the OTLP exporter construction path. Explicit parameters take precedence over the corresponding base environment variable; signal-specific environment variables (e.g. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) still take precedence over both, matching standard OTel env var rules. mTLS/certificate options remain out of scope for this convenience layer; those can still be set by constructing exporters directly and passing them via configure_otel_providers(exporters=...).
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- create_resource(): apply OTEL_RESOURCE_ATTRIBUTES as a base before explicit service_name/service_version/attributes, instead of overlaying it last, so an explicit value is never silently replaced by the environment. - create_resource(): accept resource attributes via a new attributes= dict parameter, in addition to **kwargs, so callers can pass a dictionary whose keys might collide with create_resource's own parameter names (e.g. service_name, env_file_path) without raising TypeError. Update _configure_providers() to pass resource_attributes via attributes= instead of unpacking it into **kwargs. - configure_otel_providers(): consolidate the service_name/service_version/ resource_attributes/otlp_* assignments that were duplicated across the env-file and non-env-file branches into a single shared block, since these fields don't need the env-file loading that the rest of the branch exists for. Addresses PR review comments from @moonbox3.
Evan Mattson (moonbox3)
commented
Aug 19, 2026
/review |
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: Findings reported
Scope: full PR (2 commit(s)): bcc1b96dbc0e, d2a162166467
Model:gpt-5.6-sol
Overview
The PR provides a coherent programmatic configuration path and adds useful precedence and forwarding tests, including signal-specific endpoint behavior and resource-attribute collision handling. The strongest guards are protocol normalization, protocol-specific compression mapping, optional-dependency errors, and the existing one-time setup guard. Residual risks remain around empty header overrides, credentials crossing to an environment-selected endpoint, and compatibility for callers that previously used attributes as an ordinary resource key.
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
3 verified findings remained after source verification (1 high, 2 medium) across 1 file. Details are attached to the affected lines below.
Affected areas:python/packages/core/agent_framework/observability.py
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…PR review
- _create_otlp_exporters()/_get_exporters_from_env(): an explicit
otlp_headers={} override was collapsed to None before reaching the OTLP
exporter constructors, which then fell back to reading
OTEL_EXPORTER_OTLP_HEADERS themselves (an empty dict is just as falsy as
None to their own "headers or environ.get(...)" fallback). This silently
resurrected an environment-configured credential the caller explicitly
tried to suppress. Fixed by distinguishing "not resolved" (None, exporter
may check env) from "resolved to nothing" (an authoritative {}, must not
check env) throughout, and added _shield_env()/_construct_otlp_exporter()
to temporarily hide the relevant header env vars for the duration of
construction when a signal's headers were authoritatively resolved.
- _get_exporters_from_env(): when both a programmatic otlp_endpoint and
otlp_headers are given, withhold those headers from any signal whose
endpoint resolves to a different origin (e.g. because a stray
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT points elsewhere), so a credential meant
for one collector can't be sent to a different, unintended host. Pure
env-var-driven configuration (OTEL_EXPORTER_OTLP_ENDPOINT +
OTEL_EXPORTER_OTLP_HEADERS) is unaffected and keeps its existing,
spec-conformant behavior.
- create_resource(): the new attributes= parameter previously assumed a
mapping and called dict.update() on whatever was passed, which raised
ValueError for the pre-existing call shape create_resource(attributes=
"some_value") (before attributes had a dedicated parameter, it was only
reachable via **kwargs and set a literal resource attribute named
"attributes"). Non-mapping values are now handled the same way as before,
preserving that call shape alongside the new mapping form.
Added 10 regression tests covering all three issues, verified end-to-end
against the real opentelemetry-exporter-otlp-proto-grpc/http packages.
Addresses automated review comments on PR microsoft#7703.Evan Mattson (moonbox3)
commented
Aug 19, 2026
/review |
Python Test Coverage Report •
Python Unit Test Overview
| ||||||||||||||||||||||||||||||
Evan Mattson (moonbox3)
commented
Aug 19, 2026
Dineshsuriya D (@droideronline) please fix the failing CI/CD issues |
- pyupgrade: simplify _shield_env's return annotation from Generator[None, None, None] to Generator[None], matching the codebase's existing style (py310-plus target). - pyright: cast the isinstance(attributes, Mapping)-narrowed value to Mapping[str, Any] before calling dict.update(), since attributes' declared type (Mapping[str, Any] | Any) collapses under Any and pyright otherwise narrows a bare Mapping isinstance check to Mapping[Unknown, Unknown], triggering reportUnknownArgumentType. Verified locally: pyupgrade --py310-plus is a no-op, pyright reports 0 errors on observability.py (with the grpc/http OTLP exporter packages installed, matching CI), ruff check/format clean, and all 230 tests in test_observability.py still pass.
Dineshsuriya D (droideronline)
commented
Aug 20, 2026
Fixed — both were caused by the last commit's new code, not flaky:
Verified locally: |
Evan Mattson (moonbox3)
commented
Aug 21, 2026
Tao Chen (@TaoChenOSU) can you look at this one please? |
Motivation & Context
#7702
Description & Review Guide
configure_otel_providers()— the documented one-call entry point for setting up OpenTelemetry — only readservice.name, resource attributes, and OTLP exporter settings (endpoint/protocol/headers/timeout/compression) from environment variables (OTEL_SERVICE_NAME,OTEL_RESOURCE_ATTRIBUTES,OTEL_EXPORTER_OTLP_*), even though the lower-levelcreate_resource()helper already supported passingservice_name/service_version/**attributesprogrammatically. Any caller of the documented setup path had no way to configure these in code and was forced to rely on process environment variables or a.envfile.This PR:
service_name,service_version,resource_attributes,otlp_endpoint,otlp_protocol,otlp_headers,otlp_timeout, andotlp_compressionkeyword arguments toconfigure_otel_providers()andObservabilitySettings.create_resource()(for the resource attributes) and through_get_exporters_from_env()→_create_otlp_exporters()(for the OTLP exporter settings).OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) still take precedence over both.compressionsupport ("gzip"/"deflate"/"none"), mapped to the correct enum for gRPC (grpc.Compression) vs HTTP (opentelemetry.exporter.otlp.proto.http.Compression) exporters, since the two packages use different enum types.timeout/compressionas-is (includingNone) to the OTLP exporter constructors — when left unset, the exporter classes themselves already fall back to readingOTEL_EXPORTER_OTLP_TIMEOUT/OTEL_EXPORTER_OTLP_COMPRESSION, so no env var re-parsing was needed there.configure_otel_providers(exporters=...)escape hatch.Example of what's now possible without touching any environment variables:
Added tests covering:
create_resource()/_get_exporters_from_env()param overrides taking precedence over env vars, signal-specific env vars still winning over a programmatic base override, andconfigure_otel_providers()forwarding both the resource and OTLP parameters end-to-end.Related Issue
Fixes#7702
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.