feat(otel): Zero-code auto-instrumentation via opentelemetry-distro - #2349
Merged
Conversation
…distro (#2274) The shiny[otel] extra now includes opentelemetry-distro[otlp], so `opentelemetry-instrument shiny run app.py` works out of the box. Document the zero-code path as the recommended setup and warn that manual trace.set_tracer_provider() is ignored (with a warning) when running under opentelemetry-instrument.
…Tel configuration The example unconditionally called trace.set_tracer_provider() and set_logger_provider() at import time. Providers can only be installed once per process, so under opentelemetry-instrument (or after logfire.configure()) those calls were ignored with an 'Overriding of current TracerProvider is not allowed' warning and the in-code console setup silently had no effect. The app now detects an already-configured provider and skips its in-code setup, and the docs note that OpenTelemetry must be configured in exactly one place.
…age in-code SDK config Within-app provider setup (trace.set_tracer_provider) is now documented as discouraged: it couples the app to an observability setup and conflicts with external instrumentation. All docs, best practices, backend recipes, and the open-telemetry example now use 'opentelemetry-instrument shiny run app.py' with standard OTEL_* environment variables. The example app contains no OTel setup code at all. In-code setup remains documented as a guarded fallback for platforms without launch-command control and for SDKs like logfire that manage OpenTelemetry themselves.
schloerke
marked this pull request as ready for review
July 8, 2026 21:48
Drops the lazy-initialization implementation detail, keeps a compact note on why in-code provider setup was removed from the docs (silently ignored when a provider is already installed), and points the entry at PR #2349.
The example app now displays the active SHINY_OTEL_COLLECT level in its UI, the module docstring and README document the precedence rule, and a comment at the otel.collect block explains why collect-stamped outputs still produce spans when the global level is lowered: suppress/collect are absolute per-object settings captured at creation time, taking precedence over the global level, while infrastructure spans follow only the environment variable.
This was referenced Jul 9, 2026
Closed
… docstrings Covers otel.get_level's resolution-order list, OtelCollectLevel's example fences, with_otel_span's numbered list, and the indented fences inside Troubleshooting list items.
This was referenced Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2274
Summary
Makes
opentelemetry-instrument shiny run app.pythe standard, documented way to enable OpenTelemetry, per #2274:pyproject.toml: theshiny[otel]extra now includesopentelemetry-distro[otlp], so theopentelemetry-instrumentwrapper and the OTLP exporters ship with it and the bare command works without extra flags. The explicitopentelemetry-sdk>=1.24.0floor is kept (sanitized-stack-trace requirement from ci: Test oldest supported Python and dependency versions #2335). Note:opentelemetry-distropins the SDK to a matching minor version, so combiningshiny[otel]with other SDK-pinning packages (e.g.logfire) may require matching versions.shiny/otel/__init__.pydocstring (source of the/py/api/core/OpenTelemetry.htmlreference page): auto-instrumentation is now the standard setup throughout — Quick Start, best practices, and all backend recipes (Jaeger, Honeycomb, Datadog, New Relic, console) useOTEL_*env vars + the wrapper with unmodified apps. In-codetrace.set_tracer_provider()setup is documented as discouraged, kept only as a guarded fallback for SDK-managed setups (logfire) and platforms without launch-command control. Troubleshooting covers theOverriding of current TracerProvider is not allowedconflict.examples/open-telemetry/: the example app now contains no OpenTelemetry setup code at all; the README and app docstring show the wrapper-based launch.No runtime code changes were needed: Shiny already resolves the global tracer provider lazily at span-creation time (
shiny/otel/_core.py), so the provider installed byopentelemetry-instrumentis picked up automatically.Companion PRs: posit-dev/py-shiny-site#380 (user-facing docs) and posit-dev/shiny-vscode#112 (VS Code/Positron run integration, posit-dev/shiny-vscode#111).
Verification
pip install -e ".[otel]" opentelemetry-instrument --traces_exporter console --logs_exporter console --metrics_exporter none shiny run examples/open-telemetry/app.pyInteracting with the app prints Shiny's spans (
session_start,reactive_update,session_end) and value-update log records (e.g.Set reactive.value normal_counter) to the console, with no provider-override warnings. Verified with and without--reload, and that in-codeset_tracer_provider()under the wrapper reproduces the documented override warning. Otel unit tests pass (tests/pytest/test_otel_*).