-
-
Notifications
You must be signed in to change notification settings - Fork 9k
fix(otel): one v2 logger owns the global provider; scope tenant OTLP creds per exporter #30590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
yassin-berriai
merged 8 commits into
litellm_internal_staging
from
litellm_otel_v2_per_exporter_tenant_routing
Jun 19, 2026
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3918586
fix(otel): one v2 logger owns the global provider; scope tenant creds…
yucheng-berri e2b2702
fix(otel): satisfy Any-discipline on changed lines
yucheng-berri 9bd68ec
fix(otel): annotate the untyped-global boundary with any-ok
yucheng-berri bfc916b
test(otel): cover the startup global-provider publish via injectable …
yucheng-berri de79f1f
refactor(otel): select global provider from the registered owner, not…
yucheng-berri b218a4d
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
yucheng-berri 716c870
refactor(otel): type ExporterSpec.owner as an ExporterOwner enum
yucheng-berri 68577b2
refactor(otel): rename ExporterOwner.ARIZE to ARIZE_AX
yucheng-berri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,10 @@ | ||
| """Weave (W&B) preset.""" | ||
|
|
||
| from litellm.integrations.otel.model.config import ExporterSpec, OpenTelemetryV2Config | ||
| from litellm.integrations.otel.model.config import ( | ||
| ExporterOwner, | ||
| ExporterSpec, | ||
| OpenTelemetryV2Config, | ||
| ) | ||
| from litellm.integrations.otel.presets.utils import ensure_mappers | ||
| from litellm.integrations.weave.weave_otel import ( | ||
| _get_weave_authorization_header, | ||
|
|
@@ -23,6 +27,7 @@ def weave_preset( | |
| kind=weave_cfg.protocol or "otlp_http", | ||
| endpoint=weave_cfg.endpoint, | ||
| headers=weave_cfg.otlp_auth_headers, | ||
| owner=ExporterOwner.WEAVE_OTEL, | ||
| ), | ||
| ], | ||
| # Weave consumes OpenInference + a small Weave-specific overlay. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: owner should be an enum |
||
|
|
||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -840,37 +840,6 @@ async def proxy_startup_event(app: FastAPI): | |
| if isinstance(worker_config, dict): | ||
| await initialize(**worker_config) | ||
|
|
||
| ## V2 OTEL: now that config (and therefore the callbacks) is loaded, publish | ||
| ## the chosen V2 logger's TracerProvider as the OTel global. The FastAPI | ||
| ## instrumentation mounted at app-creation binds to the global provider, so | ||
| ## this is what makes server spans and gen-ai spans share one provider and | ||
| ## land in the same trace. Prefer an already-registered preset logger | ||
| ## (arize, langfuse, …) so server spans export to that backend too; otherwise | ||
| ## build a generic one from OTEL_* envs. ``set_tracer_provider`` only takes | ||
| ## effect once, so the first configured logger wins. | ||
| try: | ||
| from litellm.integrations.otel.model.config import is_otel_v2_enabled | ||
|
|
||
| if is_otel_v2_enabled(): | ||
| from opentelemetry import trace as _otel_trace | ||
|
|
||
| from litellm.integrations.otel.logger import OpenTelemetryV2 | ||
|
|
||
| _otel_v2_logger = ( | ||
| next( | ||
| ( | ||
| cb | ||
| for cb in litellm.service_callback | ||
| if isinstance(cb, OpenTelemetryV2) | ||
| ), | ||
| None, | ||
| ) | ||
| or OpenTelemetryV2() | ||
| ) | ||
| _otel_trace.set_tracer_provider(_otel_v2_logger._tracer_provider) | ||
| except Exception as e: | ||
| verbose_proxy_logger.debug("Skipping OTel V2 provider setup: %s", e) | ||
|
|
||
| # check if DATABASE_URL in environment - load from there | ||
| if prisma_client is None: | ||
| _db_url: Optional[str] = get_secret("DATABASE_URL", None) # type: ignore | ||
|
|
@@ -907,6 +876,42 @@ async def _run_pw_migration(): | |
| redis_usage_cache=transaction_buffer_redis_cache, | ||
| ) | ||
|
|
||
| ## V2 OTEL: publish the chosen V2 logger's TracerProvider as the OTel global. | ||
| ## This MUST run after callback initialization above: a preset (arize, langfuse, | ||
| ## …) builds its logger there, folding the OTEL_* base exporter and its own | ||
| ## exporter into one logger. The FastAPI instrumentation mounted at app-creation | ||
| ## binds to the global provider, so reusing that one logger is what makes the | ||
| ## server span and the gen-ai spans share one provider and land in the same | ||
| ## trace, exporting to every configured backend. Running before callback init | ||
| ## (when no logger exists yet) would build a second, generic logger whose | ||
| ## provider became the global, orphaning the gen-ai spans onto a different | ||
| ## backend than the server span. A generic logger is built only when none was | ||
| ## configured. | ||
| try: | ||
| from litellm.integrations.otel.model.config import is_otel_v2_enabled | ||
|
|
||
| if is_otel_v2_enabled(): | ||
| from opentelemetry import trace as _otel_trace | ||
|
|
||
| from litellm.litellm_core_utils.litellm_logging import _in_memory_loggers | ||
| from litellm.integrations.otel.logger import ( | ||
| OpenTelemetryV2, | ||
| publish_global_otel_v2_provider, | ||
| ) | ||
|
|
||
| registered = ( | ||
| open_telemetry_logger | ||
| if isinstance(open_telemetry_logger, OpenTelemetryV2) | ||
| else None | ||
| ) | ||
|
Comment on lines
+902
to
+906
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not use |
||
| publish_global_otel_v2_provider( | ||
| _in_memory_loggers, # any-ok: pre-existing untyped List[Any] global | ||
| _otel_trace.set_tracer_provider, | ||
| registered=registered, | ||
| ) | ||
| except Exception as e: | ||
| verbose_proxy_logger.debug("Skipping OTel V2 provider setup: %s", e) | ||
|
|
||
| ## Validate use_redis_transaction_buffer requires Redis cache ## | ||
| ProxyStartupEvent._validate_redis_transaction_buffer_config( | ||
| general_settings=general_settings, | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why cant the callback factory just publish the provider?