Skip to content

Web UI "Trace" tab is empty because Dev UI's OpenTelemetrySdk is never bridged into com.google.adk.telemetry.Tracing #1175

@akbarkanso

Description

@akbarkanso

Describe the Bug:

When running the embedded ADK Dev UI (com.google.adk.web.AdkWebServer) inside a Spring Boot host service, the Trace tab is always empty. Requests succeed, chat reaches the agent, tools execute, but no agent/tool spans are recorded against the session.

Root cause is a wiring gap in google-adk-dev: OpenTelemetryConfig builds an OpenTelemetrySdk carrying its ApiServerSpanExporter (the bean the Trace REST controllers read from), but builds it as a non-global instance and never installs it into com.google.adk.telemetry.Tracing. ADK runtime tracing is hard-bound to GlobalOpenTelemetry.getTracer("gcp.vertex.agent"), so it emits to the JVM-wide no-op default and nothing reaches the Dev UI exporter.

Two problems compound:

  1. Misleading log line / dead branch. GlobalOpenTelemetry.get() does not throw when nothing is registered, it returns a no-op default. So the try block in openTelemetrySdk() is always entered, and a non-global SDK is always built. The IllegalStateException branch (which calls buildAndRegisterGlobal()) is effectively dead code under normal usage. The log line "OpenTelemetry already registered globally, creating non-global instance." is actively misleading: it fires even when nothing is globally registered.

  2. No Tracing.setTracerForTesting(...) call. Even if the Dev UI's local SDK were built correctly, Tracing.getTracer() continues to read from GlobalOpenTelemetry. The Dev UI's ApiServerSpanExporter therefore sees zero spans from agent invocations.

Steps to Reproduce:

  1. Embed the Dev UI in a Spring Boot service via the standard recipe (child Spring context launched from AdkWebServer).
  2. Disable any external OpenTelemetry registration (no Langfuse, no Java agent, no actuator OTel autoconfig).
  3. Boot the service, open the Dev UI on its configured port, and send a message to any agent.
  4. Open the Trace tab in the left panel.

Expected Behavior:

The Trace tab displays an agent/tool span tree for the completed invocation.

Observed Behavior:

The Trace tab is empty. No spans are recorded. With --logging.level.com.google.adk.web.config=DEBUG enabled, the following is logged:

c.g.a.w.c.OpenTelemetryConfig — Configuring SdkTracerProvider with ApiServerSpanExporter.
c.g.a.w.c.OpenTelemetryConfig — Configuring OpenTelemetrySdk and registering globally.
c.g.a.w.c.OpenTelemetryConfig — OpenTelemetry already registered globally, creating non-global instance.

The "already registered globally" branch is taken even when nothing has registered globally.

Environment Details:

  • ADK Library Version: com.google.adk:google-adk:1.2.0, com.google.adk:google-adk-dev:1.2.0
  • OpenTelemetry SDK: 1.55.0
  • Java: 21
  • Spring Boot: 4.0.5
  • OS: N/A (reproduced across environments)

Model Information: N/A (issue is independent of model)


🟡 Optional Information

Regression: Unknown — N/A to our setup.

Logs:

c.g.a.w.c.OpenTelemetryConfig — Configuring SdkTracerProvider with ApiServerSpanExporter.
c.g.a.w.c.OpenTelemetryConfig — Configuring OpenTelemetrySdk and registering globally.
c.g.a.w.c.OpenTelemetryConfig — OpenTelemetry already registered globally, creating non-global instance.

Additional Context:

Relevant decompiled sources from the affected versions:

com.google.adk.telemetry.Tracing#getTracer() (google-adk:1.2.0):

public static Tracer getTracer() {
    if (tracer == null) {
        tracer = GlobalOpenTelemetry.getTracer("gcp.vertex.agent");
    }
    return tracer;
}

com.google.adk.web.config.OpenTelemetryConfig#openTelemetrySdk(...) (google-adk-dev:1.2.0):

@Bean
public OpenTelemetry openTelemetrySdk(SdkTracerProvider sdkTracerProvider) {
    log.debug("Configuring OpenTelemetrySdk and registering globally.");
    try {
        GlobalOpenTelemetry.get(); // does NOT throw on no-op default — always enters this branch
        log.debug("OpenTelemetry already registered globally, creating non-global instance.");
        return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); // non-global
    } catch (IllegalStateException e) {
        log.debug("Registering OpenTelemetry globally.");
        OpenTelemetrySdk sdk = OpenTelemetrySdk.builder()
                .setTracerProvider(sdkTracerProvider)
                .buildAndRegisterGlobal();
        Runtime.getRuntime().addShutdownHook(new Thread(sdk::close));
        return sdk;
    }
}

Impact:

  • The Trace tab is silently broken in the most common dev setup (Dev UI as the only OTel consumer). There is no warning log pointing at the wiring gap.
  • The Trace tab is also broken when the host registers its own global SDK (e.g. Langfuse, GCP, Datadog), because the Dev UI's local non-global SDK receives no events from GlobalOpenTelemetry.
  • Affects all supported deployment shapes: standalone AdkWebServer, embedded Dev UI in a host Spring Boot context, and the runAdkDevUi Gradle task.

How often has this issue occurred?

  • ✅ Always (100%)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions