Tracing LLM Calls
Spans that tell a story
An LLM trace is a request-scoped record: parent span for the whole call, child spans for prompt assembly, the model call itself, tool invocations and streaming. Correlated ids let you replay and compare any request that matters.
What you will learn
- Model an LLM call as parent + child spans
- Attach generative-AI attributes to spans
- Correlate traces to metrics and logs
What is this?
Tracing structures per-request telemetry: one root span for the request, child spans for each stage — building the prompt, calling the model, running tools, finalizing. Gen-AI convention attributes (gen_ai.request.model, gen_ai.usage.input_tokens) ride on the model span so tools can render, search and compare them.
Why do you need it?
- Nested stages (RAG, agents) fail in different spans; a flat log can't show which hop leaked.
- Attributes let you query 'all traces using model X with temp 0.8 and slow TTFT'.
- Traces correlate: one id links the span tree, aggregate metrics and any logs.
Mental model
Tracing
A shipping manifest's packing list
The manifest (trace) references boxes (spans); each box lists contents (attributes) so you can find what moved where, and when.
How it works
- Start a root span at request entry; propagate trace/span ids via headers.
- Create child spans per stage; measure their duration and status.
- Set standard attributes: model, provider, tokens, temperature, tool calls, latency.
- Export via OTLP to a collector/backend (Langfuse, Arize, Datadog, OTel).
Span tree for one chat call.
Examples
Minimal OTel-style span
span = client.start_span("chat", attributes={ "gen_ai.request.model": "fast-model", "gen_ai.usage.input_tokens": 812, "gen_ai.usage.output_tokens": 204, "gen_ai.system": "openai",})result = call(span) # propagate trace headersspan.set_output(result) # store the textspan.end()Standard attributes keep traces queryable across providers and tools.
In production
- An agent platform shows the full loop: plan → tool → observe → next plan as one trace.
- A search team finds which retriever stage added 600ms by drilling one trace's spans.
- Langfuse/Arize UIs render prompt, tokens and verdict alongside the spans.
Best practices
- Propagate trace/span ids through async and streaming boundaries.
- Set a consistent attribute schema (semconv) across providers.
- Store truncated output plus a link to redacted full text.
- Export in batches; never block the request path on telemetry.
Common mistakes
- Starting the root span after retries — you lose the retry context.
- Not closing spans → broken durations and lost relationships.
- Credentials or full prompts in attribute values (PII leak).
Debugging tips
- One span dwarfs all → that stage is the budget hole; zoom into its own child spans.
- Missing child spans → propagation headers weren't forwarded into the worker.
- Trace differs between envs → compare attribute values (model, params, retrieval).
Interview checkpoint
Quick quiz
A parent span for a chat request most often contains what?
Traces are useful only when something errors.