Problem Context
LLM applications fail differently from traditional services. A 200 response can still be wrong, hallucinated, biased, or off-policy. CPU and memory tell you nothing. You need observability designed for non-deterministic, expensive, latency-sensitive calls where the "bug" is often a single tokens-different drift in the output.
- You ship a prompt change and discover the regression in a customer ticket three days later
- Costs jumped 40% and you can't tell which endpoint, model, or tenant
- You have logs but no way to reproduce a single user's session end-to-end
The Four Pillars of LLM Observability
- Traces โ one span per LLM call, tool call, retrieval, parsing step. Linked into a session/trace tree.
- Metrics โ latency percentiles, token counts, cost, error rate, cache hit rate. Per model, per endpoint, per tenant.
- Quality signals โ online evals (faithfulness, relevance), thumbs-up/down, user-reported issues, guardrail block rate.
- Cost telemetry โ tokens ร price ร user/tenant attribution. The metric that makes you fix things first.
flowchart LR
Q[Request] --> SP[Trace start]
SP --> RT[Retrieval span]
SP --> LLM[LLM span<br/>tokens, cost, latency]
SP --> EV[Eval span<br/>faithfulness, relevance]
SP --> SE[Trace end]
SE --> EXP[OTLP exporter]
EXP --> P[Phoenix / LangSmith /<br/>Datadog / App Insights]
Span Attributes That Earn Their Keep
Every LLM span should carry, at minimum:
gen_ai.system "openai" | "anthropic" | "azure_openai"
gen_ai.request.model "gpt-4o-2024-08-06"
gen_ai.request.temperature 0.2
gen_ai.usage.input_tokens 1248
gen_ai.usage.output_tokens 312
gen_ai.usage.cached_tokens 1024
gen_ai.response.finish_reason "stop" | "length" | "tool_calls"
gen_ai.cost.usd 0.0021
user.id / tenant.id (hashed)
prompt.template.id "support_v3"
prompt.template.version "3.2.1"The OpenTelemetry gen_ai.* semantic conventions are the emerging standard โ adopt them now so your vendor choice is reversible.
Sampling: 100% Traces Are a Trap
Tracing every call at scale is expensive in storage and slow at query time. Sane defaults:
- 100% sampling for errors, guardrail blocks, and negative user feedback.
- 100% for tenants on enterprise tier (you're accountable to them).
- Tail-based sampling for the rest: 10% baseline, boost to 100% if latency > p99 or cost > threshold.
- Always store the full prompt+response for sampled traces โ without it the trace is useless.
Cost Telemetry Is the First Dashboard
Build the cost dashboard before anything else. Group by (model, endpoint, tenant), day-over-day. The moment a tenant 2ร their spend overnight or a new endpoint quietly burns $500/day, you need to see it. Cost is also the proxy that catches most quality regressions โ retries spike before users notice.
Online Evals: The Quality Pulse
Offline evals (a fixed eval set in CI) catch regressions before shipping. Online evals catch the regressions that only happen on real traffic. Run a lightweight eval (faithfulness, relevance, schema validity) on 1โ5% of production traffic and emit it as a metric. When it drops, you have a window of hours rather than days to react.
Vendor Landscape (2026)
- LangSmith โ deepest integration with LangChain/LangGraph; strong eval workflows.
- Phoenix (Arize) โ open-source, OTLP-native; great for self-hosted, model-agnostic.
- Langfuse โ open-source + cloud; pragmatic prompt versioning + evals.
- Datadog LLM Observability / App Insights โ if you already pay them for everything else, the cheapest path to one pane of glass.
- Helicone / Traceloop / Honeyhive โ viable niche options; pick on integration surface, not feature list.
Failure Modes
- Storing raw prompts with PII โ observability becomes a compliance liability. Hash or redact at the SDK level.
- No trace_id propagation โ you can't tie a user complaint to a trace. Always thread
trace_idthrough your API responses. - Metrics without dimensions โ "average latency" with no model/endpoint split is useless.
- Eval drift โ your online eval prompt itself changes and skews the metric. Version the eval prompt.
Production Checklist
- OpenTelemetry with
gen_ai.*attributes, one span per LLM call. - Cost-by-tenant dashboard before anything else.
- Tail-based sampling with full prompt/response capture on the sampled set.
- Online evals on 1โ5% of traffic, emitted as metrics.
trace_idreturned in every API response header for support workflows.
Key Takeaways
- LLM observability is traces + cost + quality, not just APM extended.
- Adopt
gen_ai.*conventions so your tooling choice stays portable. - Online evals close the loop that offline evals can't โ ship both.

