Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/google/adk/telemetry/google_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def get_gcp_exporters(
if enable_cloud_tracing:
span_processor = _get_gcp_span_exporter(credentials)
span_processors.append(span_processor)
if user_span_processor := _maybe_get_configured_otlp_span_processor():
span_processors.append(user_span_processor)
Comment on lines +142 to +143

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This change is redundant and introduces a bug where spans are exported twice to the configured OTLP endpoint.\n\nIn src/google/adk/telemetry/setup.py, the maybe_set_otel_providers function already automatically appends generic OTel exporters (including the OTLP span exporter) by calling _get_otel_exporters():\n\npython\n # Add generic OTel exporters based on OTel env variables.\n hooks_to_setup.append(_get_otel_exporters())\n\n\nIf a user configures OTEL_EXPORTER_OTLP_ENDPOINT and calls maybe_set_otel_providers([get_gcp_exporters(...)]), both get_gcp_exporters and _get_otel_exporters will register a BatchSpanProcessor(OTLPSpanExporter()). This results in duplicate span processors being registered to the TracerProvider, causing every span to be exported twice to the user's OTLP pipeline.\n\nSince the existing setup already correctly tees spans to the standard OTLP endpoint when the environment variables are set, we should revert this change and keep get_gcp_exporters strictly scoped to GCP-specific exporters.


metric_readers: list[MetricReader] = []
if enable_cloud_metrics:
Expand All @@ -160,6 +162,31 @@ def get_gcp_exporters(
)


def _maybe_get_configured_otlp_span_processor() -> Optional[SpanProcessor]:
"""Returns a span processor for a user-configured OTLP endpoint, or None when
OTEL_EXPORTER_OTLP_(TRACES_)ENDPOINT is not set.

Lets an existing OTLP pipeline receive spans alongside the GCP exporters. The
OTLPSpanExporter reads OTEL_EXPORTER_OTLP_* (endpoint, headers, timeout)
itself; this only wires it into the provider.
"""
if not (
os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
):
return None
try:
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
except (ImportError, AttributeError):
logger.warning(
"OTEL_EXPORTER_OTLP_ENDPOINT is set but"
" opentelemetry-exporter-otlp-proto-http is not installed;"
" skipping the additional OTLP span exporter."
)
return None
return BatchSpanProcessor(OTLPSpanExporter())
Comment on lines +165 to +187

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This helper function is no longer needed since the OTLP span exporter is already configured and managed in src/google/adk/telemetry/setup.py via _get_otel_exporters().



def _get_gcp_span_exporter(credentials: Credentials) -> SpanProcessor:
"""Adds OTEL span exporter to telemetry.googleapis.com"""

Expand Down
Loading