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:
-
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.
-
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:
- Embed the Dev UI in a Spring Boot service via the standard recipe (child Spring context launched from
AdkWebServer).
- Disable any external OpenTelemetry registration (no Langfuse, no Java agent, no actuator OTel autoconfig).
- Boot the service, open the Dev UI on its configured port, and send a message to any agent.
- 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?
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:OpenTelemetryConfigbuilds anOpenTelemetrySdkcarrying itsApiServerSpanExporter(the bean the Trace REST controllers read from), but builds it as a non-global instance and never installs it intocom.google.adk.telemetry.Tracing. ADK runtime tracing is hard-bound toGlobalOpenTelemetry.getTracer("gcp.vertex.agent"), so it emits to the JVM-wide no-op default and nothing reaches the Dev UI exporter.Two problems compound:
Misleading log line / dead branch.
GlobalOpenTelemetry.get()does not throw when nothing is registered, it returns a no-op default. So thetryblock inopenTelemetrySdk()is always entered, and a non-global SDK is always built. TheIllegalStateExceptionbranch (which callsbuildAndRegisterGlobal()) 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.No
Tracing.setTracerForTesting(...)call. Even if the Dev UI's local SDK were built correctly,Tracing.getTracer()continues to read fromGlobalOpenTelemetry. The Dev UI'sApiServerSpanExportertherefore sees zero spans from agent invocations.Steps to Reproduce:
AdkWebServer).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=DEBUGenabled, the following is logged:The "already registered globally" branch is taken even when nothing has registered globally.
Environment Details:
com.google.adk:google-adk:1.2.0,com.google.adk:google-adk-dev:1.2.01.55.0Model Information: N/A (issue is independent of model)
🟡 Optional Information
Regression: Unknown — N/A to our setup.
Logs:
Additional Context:
Relevant decompiled sources from the affected versions:
com.google.adk.telemetry.Tracing#getTracer()(google-adk:1.2.0):com.google.adk.web.config.OpenTelemetryConfig#openTelemetrySdk(...)(google-adk-dev:1.2.0):Impact:
GlobalOpenTelemetry.AdkWebServer, embedded Dev UI in a host Spring Boot context, and therunAdkDevUiGradle task.How often has this issue occurred?