This module provides OpenTelemetry observability integration for A2A servers and clients, including distributed tracing and context propagation.
- Distributed Tracing: Automatic span creation for all A2A protocol methods (both client and server)
- Context Propagation: W3C Trace Context injection into outbound client requests
- Request/Response Logging: Optional extraction of request and response data into span attributes
- Error Tracking: Automatic error status and error type attributes on failures
Constants and span attribute names shared across client and server modules. See A2AObservabilityNames for the full list of attribute keys.
Client-side distributed tracing. Wraps ClientTransport to create spans for every A2A operation. Discovered automatically via ServiceLoader when a Tracer is present in the transport configuration parameters.
Injects W3C Trace Context headers into outbound client requests using OpenTelemetry.getPropagators().getTextMapPropagator(). Discovered automatically via ServiceLoader when an OpenTelemetry instance is present in the transport configuration parameters.
CDI decorator (OpenTelemetryRequestHandlerDecorator) that wraps RequestHandler to create server-side spans. Automatically activated when the module JAR is on the classpath (via the bundled beans.xml).
Quarkus-based integration tests for OpenTelemetry functionality.
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-extras-opentelemetry-server</artifactId>
<version>${a2a.version}</version>
</dependency>The OpenTelemetryRequestHandlerDecorator is registered automatically via the beans.xml bundled in the module JAR. No additional configuration is required.
Note: The decorator relies on the runtime (e.g., Quarkus OpenTelemetry extension) to extract trace context from incoming HTTP requests. It does not perform context extraction itself — it creates a new span within the already-established trace context.
Optionally include full request/response data as span attributes. These are controlled by JVM system properties (not application config), read via Boolean.getBoolean():
# Pass as JVM system properties (-D flags)
-Dorg.a2aproject.sdk.server.extract.request=true
-Dorg.a2aproject.sdk.server.extract.response=trueWarning: Extracting request/response data may expose sensitive information in traces. Use with caution in production environments.
The following attributes are automatically added to spans (defined in A2AObservabilityNames):
For streaming methods (onMessageSendStream, onSubscribeToTask), the server-side span covers only the creation of the Flow.Publisher, not the actual streaming of events. The span is started and ended synchronously before events are emitted, and the response attribute is set to "Stream publisher created". This means the span duration does not reflect the actual duration of the streaming operation.
On the client side, streaming is handled differently: the OpenTelemetryClientTransport creates child spans linked to the parent for each streaming event and error callback, providing per-event tracing.
Add the client tracing module:
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-extras-opentelemetry-client</artifactId>
<version>${a2a.version}</version>
</dependency>Enable tracing by adding a Tracer instance to the transport configuration parameters:
import static org.a2aproject.sdk.extras.opentelemetry.client.OpenTelemetryClientTransportWrapper.OTEL_TRACER_KEY;
config.setParameters(Map.of(
OTEL_TRACER_KEY, openTelemetry.getTracer("my-service")
));Add the propagation module:
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-extras-opentelemetry-client-propagation</artifactId>
<version>${a2a.version}</version>
</dependency>Enable propagation by adding an OpenTelemetry instance to the transport configuration parameters:
import static org.a2aproject.sdk.extras.opentelemetry.client.propagation.OpenTelemetryClientPropagatorTransportWrapper.OTEL_OPEN_TELEMETRY_KEY;
config.setParameters(Map.of(
OTEL_TRACER_KEY, openTelemetry.getTracer("my-service"),
OTEL_OPEN_TELEMETRY_KEY, openTelemetry
));Both wrappers are discovered automatically via ServiceLoader and activate only when their respective keys are present in the configuration parameters. The client wrapper (priority 600) runs after the propagation wrapper (priority 500), so trace context headers are injected before the tracing span is created.
Client Request
↓
Runtime (e.g., Quarkus OTel) extracts trace context from HTTP headers
↓
OpenTelemetryRequestHandlerDecorator creates span within trace context
↓
Default RequestHandler
↓
Agent Execution
↓
Response
Application calls ClientTransport method
↓
OpenTelemetryClientPropagatorTransport injects W3C trace context headers (priority 500)
↓
OpenTelemetryClientTransport creates tracing span (priority 600)
↓
Underlying transport (JSON-RPC, gRPC, REST)
↓
Response
The module includes:
OpenTelemetryRequestHandlerDecoratorTest: Tests for span creation and error handling
Run tests:
mvn test -pl extras/opentelemetry/serverSymptom: Spans in async tasks are not linked to parent spans.
Solution: Ensure your runtime provides trace context propagation for async boundaries. For Quarkus, this is handled automatically by MicroProfile Context Propagation. The context-aware ManagedExecutor that propagates trace context across async boundaries is provided by the reference server module (reference/common), not this OpenTelemetry module.
Symptom: Increased latency with OpenTelemetry enabled.
Solution:
- Disable request/response extraction in production
- Configure sampling rate to reduce trace volume
- Ensure your OpenTelemetry collector is properly sized
- Sampling: Configure appropriate sampling rates for production environments
- Sensitive Data: Disable request/response extraction if handling sensitive data
- Resource Attributes: Add service name and version as resource attributes
- Collector Configuration: Use batch processors to reduce network overhead
- Monitoring: Monitor the OpenTelemetry collector's health and performance
- OpenTelemetry API
- A2A Server Common (for server module)
- A2A Client Transport SPI (for client modules)
