[Server] Carry trace context through a request (#452)

traceparent, tracestate and baggage reach handlers through RequestContext
and are echoed onto the notifications the request causes, so a span is not
broken by the response stream. Passed through verbatim: what they mean is
the tracing library's business, and no dependency is taken for it.
This commit is contained in:
Christopher Hertel 2026-08-19 00:20:32 +02:00 • committed by GitHub
commit 855011b8dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -594,7 +594,7 @@ final class StatelessProtocol
{
try {
while ($run->valid()) {
yield $run->current()->jsonSerialize();
yield self::withTraceContext($run->current()->jsonSerialize(), $meta->traceContext);
$run->next();
}
@ -654,6 +654,31 @@ final class StatelessProtocol
);
}
/**
* Puts the request's trace context back onto a notification it caused, so a
* collector can join the two without the handler carrying it by hand.
*
* @param array<string, mixed> $frame
* @param array<string, string> $traceContext
*
* @return array<string, mixed>
*/
private static function withTraceContext(array $frame, array $traceContext): array
{
if ([] === $traceContext) {
return $frame;
}
$params = \is_array($frame['params'] ?? null) ? $frame['params'] : [];
$frameMeta = \is_array($params['_meta'] ?? null) ? $params['_meta'] : [];
// Anything the notification set itself wins: it knows its own span.
$params['_meta'] = [...$traceContext, ...$frameMeta];
$frame['params'] = $params;
return $frame;
}
/**
* The one place a handler's exception becomes an answer, so the streaming
* and non-streaming paths cannot disagree about which code it earns.