feat: Propagate W3C trace context headers from clients

This extracts the W3C trace context headers (traceparent and
tracestate) from incoming requests, stuffs them as attributes on the
spans we create, and uses them within the tracing provider
implementation to actually wrap our spans in the proper context.

What this means in practice is that when a client (such as an OpenAI
client) is instrumented to create these traces, we'll continue that
distributed trace within Llama Stack as opposed to creating our own
root span that breaks the distributed trace between client and server.

It's slightly awkward to do this in Llama Stack because our Tracing
API knows nothing about opentelemetry, W3C trace headers, etc - that's
only knowledge the specific provider implementation has. So, that's
why the trace headers get extracted by in the server code but not
actually used until the provider implementation to form the proper
context.

This also centralizes how we were adding the `__root__` and
`__root_span__` attributes, as those two were being added in different
parts of the code instead of from a single place.

Fixes #2097

Signed-off-by: Ben Browning <bbrownin@redhat.com>
This commit is contained in:
Ben Browning 2025-05-13 08:36:58 -04:00
parent 473a07f624
commit dd57eff47b
3 changed files with 33 additions and 4 deletions

View file

@ -34,6 +34,8 @@ logger = get_logger(__name__, category="core")
INVALID_SPAN_ID = 0x0000000000000000
INVALID_TRACE_ID = 0x00000000000000000000000000000000
ROOT_SPAN_MARKERS = ["__root__", "__root_span__"]
def trace_id_to_str(trace_id: int) -> str:
"""Convenience trace ID formatting method
@ -178,7 +180,8 @@ async def start_trace(name: str, attributes: dict[str, Any] = None) -> TraceCont
trace_id = generate_trace_id()
context = TraceContext(BACKGROUND_LOGGER, trace_id)
context.push_span(name, {"__root__": True, **(attributes or {})})
attributes = {marker: True for marker in ROOT_SPAN_MARKERS} | (attributes or {})
context.push_span(name, attributes)
CURRENT_TRACE_CONTEXT.set(context)
return context