You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c56957f
Browse filesBrowse the repository at this point in the historyBrowse files
feat(opentelemetry): add streaming spans, metrics, and push tracing
- Two-span model for server streaming: method span + closing span with events and link
- Client streaming events recorded as span events on the parent span
- gen_ai.client.operation.duration and gen_ai.agent.a2a.streaming.duration histograms
- OpenTelemetryPushNotificationSenderDecorator with CLIENT spans for push delivery
Signed-off-by: Emmanuel Hugonnet <ehugonne@redhat.com>
Copy file name to clipboardExpand all lines: docs/content/dev/extra/opentelemetry.md
+104-1Lines changed: 104 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,6 +14,7 @@ Adds distributed tracing and context propagation to A2A servers and clients usin
14
14
-**Context Propagation**: OpenTelemetry trace context propagation across async operations
15
15
-**Request/Response Logging**: Optional extraction of request and response data into spans
16
16
-**Error Tracking**: Automatic error status and error type attributes on failures
17
+
-**Metrics**: `gen_ai.client.operation.duration` histogram on the client side; `gen_ai.agent.a2a.streaming.duration` histogram for full streaming lifecycle on the server side
Streaming methods (`message/stream` and `tasks/resubscribe`) follow a two-span model to correctly represent the full duration of a streaming response.
72
+
73
+
**Span 1 — method span** is created when the request arrives and ended as soon as the publisher is returned to the transport layer. It captures request attributes and the initial outcome (success or synchronous error).
74
+
75
+
**Span 2 — end span** (`<method>-end`) is created when the stream terminates — either on completion or error. It carries a [link](https://opentelemetry.io/docs/concepts/signals/traces/#span-links) to Span 1 for correlation, and records every A2A event that was published as a [span event](https://opentelemetry.io/docs/concepts/signals/traces/#span-events).
76
+
77
+
<preclass="mermaid">
78
+
sequenceDiagram
79
+
participant C as Client
80
+
participant D as OTel Decorator
81
+
participant H as RequestHandler
82
+
participant W as WrappedPublisher
83
+
84
+
C->>D: onMessageSendStream(params)
85
+
D->>D: create Span₁ (SERVER)
86
+
D->>H: onMessageSendStream(params)
87
+
H-->>D: publisher
88
+
D->>D: capture Span₁.context
89
+
D->>D: Span₁.setStatus(OK) · Span₁.end()
90
+
D-->>C: WrappedPublisher
91
+
92
+
note over D: Span₁ closed immediately
93
+
94
+
loop for each streaming event
95
+
W-->>C: onNext(event)
96
+
W->>W: collect PendingEvent(name, attributes)
97
+
end
98
+
99
+
alt stream completes normally
100
+
W->>W: create Span₂ (SERVER, link→Span₁)
101
+
W->>W: addEvent per PendingEvent
102
+
W->>W: Span₂.setStatus(OK) · Span₂.end()
103
+
W-->>C: onComplete()
104
+
else stream errors
105
+
W->>W: create Span₂ (SERVER, link→Span₁)
106
+
W->>W: addEvent per PendingEvent
107
+
W->>W: Span₂.setStatus(ERROR) · Span₂.end()
108
+
W-->>C: onError(throwable)
109
+
end
110
+
</pre>
111
+
112
+
Each span event on Span₂ carries:
113
+
114
+
| Attribute | Value |
115
+
|-----------|-------|
116
+
|`gen_ai.agent.a2a.streaming-event`|`toString()` of the published `StreamingEventKind`|
117
+
|`gen_ai.agent.a2a.status.code`|`OK`|
118
+
119
+
### Metrics
120
+
121
+
The server decorator records the following histogram instruments:
122
+
123
+
| Instrument | Type | Unit | Description |
124
+
|------------|------|------|-------------|
125
+
|`gen_ai.agent.a2a.streaming.duration`| Histogram |`s`| Duration from stream initiation to last event, for `message/stream` and `tasks/resubscribe`|
126
+
127
+
Attributes on `gen_ai.agent.a2a.streaming.duration`:
128
+
129
+
| Attribute | Value |
130
+
|-----------|-------|
131
+
|`gen_ai.agent.a2a.operation.name`| A2A method name (`message/stream` or `tasks/resubscribe`) |
132
+
|`gen_ai.system`|`a2a`|
133
+
|`error.type`|`error` (only on stream failure) |
134
+
135
+
Meter injection uses CDI `Instance<Meter>`, so the histogram is silently skipped if no `Meter` bean is available in the container.
136
+
68
137
### Context-Aware Async Executor
69
138
70
139
> **Note:** The `AsyncManagedExecutorProducer` is provided by the **Quarkus reference server** ([`reference/common`](https://github.com/a2aproject/a2a-java/blob/main/reference/common/src/main/java/org/a2aproject/sdk/server/common/quarkus/AsyncManagedExecutorProducer.java)), not the OpenTelemetry module. It is documented here because it enables context propagation (including trace context) across async boundaries.
@@ -106,6 +175,19 @@ The following attributes are automatically added to spans (defined in `A2AObserv
106
175
|`gen_ai.agent.a2a.response`| Full response data (only if extraction enabled) |
107
176
|`error.type`| Error message (on failures) |
108
177
178
+
### Push Notification Delivery Tracing
179
+
180
+
When the server delivers a push notification to a client webhook, `OpenTelemetryPushNotificationSenderDecorator` creates a `CLIENT` span named `SendPushNotification`. This span is independent of the original request span but carries the `gen_ai.agent.a2a.task_id` attribute, allowing correlation by task ID in your observability tool.
Copy file name to clipboardExpand all lines: examples/helloworld/client/src/main/java/org/a2aproject/sdk/examples/helloworld/client/HelloWorldClient.java
0 commit comments