Skip to content

feat: Open Telemetry: support declarative configuration via OTEL_CONFIG_FILE - #431

Draft
adubovik wants to merge 10 commits into
developmentfrom
feat/support-declarative-configuration
Draft

adubovik wants to merge 10 commits into
developmentfrom
feat/support-declarative-configuration

Conversation

@adubovik

Copy link
Copy Markdown
Collaborator

Applicable issues

N/A

Description of changes

The details are in the new docs/open_telemetry.md.

Checklist

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

🤖 Generated with Claude Code

adubovik and others added 10 commits August 10, 2026 18:19
Regressions of the declarative telemetry configuration, each caught by an
existing test:

* The one-line console exporter wrote to stdout, splitting the app's log
  stream in two: stdlib logging, uvicorn and the console takeover all work
  on stderr.
* configure_sdk() ran before the console was freed, so the root handler
  installed by the logging instrumentor via basicConfig() was removed
  right after it had been installed, silencing log correlation entirely.
* _takes_over_console() was asked after _enrich_configuration() had moved
  the console exporter into additional_properties, so it never saw one and
  a competing handler kept logging next to the OTel one.
* opentelemetry-instrumentation-logging >=0.64b0 only injects the otel*
  record fields when asked to; set_logging_format alone no longer implies
  inject_trace_context.

The HTTP client instrumentors are gated on the tracer provider again: a
logs-only app has no use for them. The instrumentation of languages other
than Python, which only a configuration file can carry, is now preserved
instead of being dropped by the enrichment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An empty dict is the only shape the console exporter ever has -- `console:
{}` in the schema means "console exporter, no options", and the enrichment
writes the one-line replacement as `{}` too. Deciding on the truthiness of
either left _takes_over_console() blind to both, so a competing stderr
handler kept logging next to the OTel one; presence is what to check.

Also raise the floor of the core OpenTelemetry packages to 1.44.0, the
release the lock file resolves to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
configure_sdk_logger() used to guess, from the OTEL_* variables alone, whether
init_telemetry() would install a root handler, and stepped aside when it
thought so. The guess has no way to be right: telemetry is opt-in through
DIALApp(telemetry_config=...), so OTEL_LOGS_EXPORTER=console without one left
nobody owning the console -- INFO records were dropped and WARNING ones leaked
through logging's unformatted lastResort -- while a configuration file, which
the variables cannot describe, went unnoticed and had its correlated format
overwritten by configure_root_logger().

Only _takes_over_console() knows the answer, so init_telemetry() now reports
it and the guess is gone. Being wrong in the remaining direction costs
nothing: should OTel claim the console later, the takeover drops the handlers
the SDK had installed, which is what it is there for.

route_sdk_loggers_to_root() becomes the single implementation of the rerouting
configure_root_logger() open-coded, over the same set of loggers -- the
takeover no longer leaves uvicorn.access and uvicorn.error rendering on their
own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The flag belongs to log_config: its reader must not import the telemetry
module, which needs the optional extra and would close an import cycle
through route_sdk_loggers_to_root(). Keeping it there but writing it from
init_telemetry() as a module attribute is what the sys.modules lookup was
trading against, so name it private and let init_telemetry() ask for the
takeover by name instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing in the test suite fails if the call is turned back into a module
constant -- every configuration-file test sets the variable in a subprocess
environment, where an eager read is indistinguishable from a late one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Since 0.64b0 the logging instrumentor installs a log handler of its own, next
to the one the SDK adds for its logger provider, so every record was exported
twice -- to the console and to the collector -- whenever tracing and log export
were enabled together. No test caught it: the console tests never turned
tracing on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/open_telemetry.md covers TelemetryConfig, the OTEL_* variables and
OTEL_CONFIG_FILE, how they take precedence over one another, the
instrumentation the SDK installs by default, and the one-line console log
exporter. The same configuration is spelled out both as variables and as a
file, and a metric view shows what only a file can express.

Two claims in docs/logging.md no longer held: nothing warns when the OTEL_*
variables are set next to a configuration file, and instrumentation is no
longer opt-in per library on the file path -- the SDK adds the same defaults
there. Log correlation, which a file does have to ask for, is documented in
their place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@adubovik
adubovik marked this pull request as draft August 12, 2026 12:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant