The Monitoring & Observability Layer
Introduction
We've built every layer of the architecture — gateway (L219), cache (L220), orchestration (L221), guardrails (L222). This final lesson adds the one that lets you operate and improve all of them: observability — the eyes of the system. Chip Huyen's rule from L218 bears repeating: "integrate it from the beginning, not as an afterthought," and its importance "grows with the complexity of the system."
Here's the trap this lesson exists to break: you can stand up a working LLM app with zero visibility into whether it's any good. It returns
200 OK, the latency looks fine — and meanwhile it's hallucinating to a third of your users, burning 3× the budget on bloated prompts, and you have no idea, because you're watching the wrong things. Traditional monitoring tells you the system is up. LLM observability tells you the system is right.
In this lesson:
- Why LLM observability ≠ traditional monitoring — the shift from "is it up?" to "is it right, safe, and cheap?"
- The three pillars — metrics, logs, and traces, LLM-flavored
- Traces are the centerpiece — a span per pipeline step, with per-span cost, latency, and quality
- What to measure — and the observability → evaluation loop that makes the system improve
- The reference architecture, complete — what all six lessons add up to
Scope: this is the observability layer — what to watch and how to see it. Deep evaluation methodology, plus audit logs / incident response / governance, get fuller treatment in the Guardrails, Safety, Reliability & Governance section; here we cover observability and the connection to eval. Latency metrics (TTFT/TPOT) are L207, cost is L213 — we watch them here.

Why LLM Observability ≠ Traditional Monitoring
If you've run a web service, you know APM (Datadog, New Relic): track latency, error rate, throughput, uptime — and you know the system is healthy. That's necessary for an LLM app too, but it's dangerously incomplete, because of one fact: an LLM app can be 100% "healthy" and 100% wrong.
| Traditional monitoring (APM) | LLM observability | |
|---|---|---|
| The question | Is it up? Is it fast? | Is it right, safe, and cheap? |
| Watches | latency, errors, throughput, uptime | + quality, cost/tokens, safety, hallucination |
| Failure it catches | a 500, a timeout | a confident wrong answer (returns a clean 200) |
| Audience | SREs / ops | + ML engineers, data scientists, product |
The core difference: traditional monitoring validates expected, deterministic behavior — did the request succeed? LLM observability has to monitor stochastic outputs — was the generated answer actually correct, grounded, and on-budget? A hallucination, a prompt-injection that slipped a guard, a silent quality regression after a model update — none of these throw an error. They return 200 OK. If you only watch the APM dashboard, you'll never see them.
The mental upgrade: stop asking "is the system working?" and start asking "is the system doing the right thing — and what is each request actually costing me?" Everything below is how you answer that.
The Three Pillars — Metrics, Logs, Traces
Observability still rests on the classic three pillars — but each is adapted to the LLM workload:
- Metrics — aggregate numbers over time. Beyond the system metrics (latency, throughput, error rate) you add LLM-specific ones: quality (hallucination rate, relevance, eval scores), cost (tokens and $ per request — L213), safety (guardrail block / false-positive rate — L222), and cache hit rate (L220). Always broken down by user, prompt version, model, and release — so you can see which change moved the number.
- Logs — the full record of what happened. The guidance is blunt: "log everything" — the prompt, the completion, intermediate outputs, retrieved context, configs, the tool calls. Tag and ID every log so you can trace its origin. And the most underrated practice in the whole lesson: manually read your production data, daily — nothing surfaces problems faster than actually looking at what users send and what the model says back.
- Traces — the full path of one request through every component. This is the pillar that changes the most for LLM apps, and the one worth the deepest look — next.
Metrics tell you something is wrong (the hallucination rate jumped). Logs let you read what happened. But traces tell you exactly where in the pipeline it went wrong — which is why, for a multi-step LLM app, the trace is the star.
Traces Are the Centerpiece
Remember the pipeline from L221 — gateway → guardrails → router → retrieve → model → guardrails? A trace is that pipeline, recorded: every step becomes a span, and each span carries its own latency, cost, and status. A real production trace has 30 to 300 spans (agents and multi-step RAG balloon fast), and the 2026 standard attributes cost per span, not just per request.
Why this matters: a request that returns "200 OK, 1.9s" is a black box — you can't act on it. The trace turns it into a glass box that answers the three questions APM can't:
- Where did the time go? → the span with the longest bar is your latency bottleneck (often a retrieval or a slow tool).
- Where did the money go? → the span with the highest cost is your cost driver (usually the model call — and often because the context got bloated upstream).
- Where did quality break? → the span an evaluator flagged (a hallucination, a failed grounding check) tells you the exact step that produced the bad output.
You instrument this with OpenTelemetry's GenAI semantic conventions (gen_ai.*) — a vendor-neutral standard so your traces work in any tool:
from opentelemetry import trace
tracer = trace.get_tracer("llm-app")
# Each pipeline step is a SPAN; attributes carry tokens, cost, latency, quality.
def answer(query: str) -> str:
with tracer.start_as_current_span("request") as root: # the whole trace
ctx = retrieve(query) # ← its own child span (slow? = bottleneck)
with tracer.start_as_current_span("model.generate") as span:
out = model(build_prompt(query, ctx))
span.set_attribute("gen_ai.usage.input_tokens", out.usage.input)
span.set_attribute("gen_ai.usage.output_tokens", out.usage.output)
span.set_attribute("gen_ai.cost_usd", out.cost) # ← per-SPAN cost (the 2026 standard)
verdict = output_guardrail(out) # ← its own span
span.set_attribute("eval.hallucination", verdict.flagged) # ← quality on the span → feeds eval (next)
return out.text
# A tool (LangSmith / Langfuse / Helicone / Arize / Datadog) ingests these spans and renders the waterfall.That eval.hallucination attribute is the hinge to the next idea: a span isn't just a performance record, it's a quality record — and quality records are what turn observation into improvement.
What to Measure — Quality, Cost, Safety, System
Observability is the layer where every lever from this whole course shows up as a number you watch. A practical dashboard for an LLM app tracks four families:
- Quality (the one APM ignores) — hallucination / groundedness rate, relevance, task success, and user feedback (thumbs up/down). The hardest to measure and the most important — it's "is the answer right?"
- Cost — tokens in/out and $ per request (L213), broken down by feature and model; plus cache hit rate (L220) and routing mix (L216). This is how you catch the "a prompt change 3×'d our bill" regression.
- Safety — guardrail block rate and false-positive rate (L222), policy violations caught, jailbreak attempts. Tells you both "are we blocking bad traffic?" and "are we over-blocking good traffic?"
- System — latency (TTFT / TPOT, L207), throughput, error rate, uptime. The classic APM metrics — still necessary, just no longer sufficient.
Two rules make metrics useful instead of noise. (1) Segment everything — by prompt version, model, release, and user cohort — so a number that moves points at a cause. (2) Alert on the right signal — a spike in hallucination rate or cost/request matters far more than a 50ms latency wobble. Watch what actually breaks an LLM product, not just what breaks a web service.
The Observability → Evaluation Loop
Here's what makes LLM observability generative rather than just diagnostic: production data is your best evaluation data. The most valuable thing observability produces isn't a dashboard — it's a feedback loop that makes the next version better.
- Online evals — run a quality scorer (an LLM-as-judge, a heuristic, a classifier) on a sampled slice of live traffic, continuously. This catches drift — a silent quality drop after a prompt tweak or a model update — as it happens, not in next quarter's user complaints.
- Tail-based sampling — you can't store/score every span at scale, so sample smartly: keep every interesting span — the ones where the hallucination evaluator fired, cost blew the budget, or the user hit thumbs-down — and sample the boring rest at 1–5%. (Traditional head-based sampling keeps a random 1–10% — and misses exactly the failures you care about.)
- Traces → eval datasets — and here's the loop closing: the flagged production traces become test cases. A real failure a user hit today becomes a row in your eval set that every future deploy is tested against. "What you observe in production becomes what you test against before the next deployment."
This is the engine of a system that gets better over time: observe → flag the failures → turn them into evals → fix → re-deploy → observe again. Observability without this loop is just watching a system degrade; with it, every production failure is a permanent regression test. (The deep eval methodology — judges, metrics, datasets — is the Guardrails/Safety & eval material; the wiring lives here.)
See It — Black Box vs Glass Box
Take one request and look at it two ways — the way traditional monitoring sees it, and the way an LLM trace sees it:

The toggle is the lesson. Both views describe the same 200 OK, 1.9s — but the black box stops there, while the trace decomposes it into spans and surfaces the three things you actually need: the bottleneck (web retrieval, 63% of the latency), the cost driver (the model call, 94% of the spend), and the quality failure (a hallucination the guardrail flagged → straight into the eval set). You can't fix what you can't see — and 200 OK shows you nothing.
The Reference Architecture, Complete
This lesson closes the AI Application Architecture section — so step back and see the whole thing you've built. We started (L218) with a bare model call and a promise: add each layer only when you hit the problem it solves. Six lessons later, the production architecture is a model wrapped in five layers:

Each layer earned its place by solving a specific problem: the gateway tamed multi-provider chaos and added fallback; the cache cut latency and cost; orchestration wired it into a fast parallel pipeline; guardrails made it safe; and observability makes the whole thing operable and improvable. Notice observability wraps everything — because every other layer reports into it, and it's the layer that tells you whether the others are working.
And the meta-lesson holds: you don't build this on day one. You grow into it, layer by layer, driven by what your observability shows you. That's why observability is "integrate from the beginning" — it's the layer that tells you which layer to add next. The architecture is a destination you navigate to with data, not a checklist you build up front.
🧪 Try It Yourself
Reason through these, then confirm with the Trace Lab:
- Predict: your app returns
200 OKin 1.9s and a user complains the answer was wrong. Your APM dashboard is all green. What can it tell you, and what can it not? - In the lab's full trace, three spans light up. Which one is the latency problem, which is the cost problem, and which is the quality problem — and would any of them show up in traditional monitoring?
- You can only afford to store-and-score 3% of traffic. Random 3% (head-based) vs tail-based sampling — which do you pick, and what does it keep?
- A prompt tweak ships Monday; by Friday, quality has quietly dropped 15% with no errors and no latency change. What catches this, and how?
- A user hits thumbs-down on a bad answer. Beyond logging it, what's the highest-leverage thing to do with that trace?
→ (1) APM tells you it was up and how long it took — and nothing about which step was slow, what it cost, or whether the answer was correct. A clean 200 hides a wrong answer. (2) Latency = retrieve·web (the longest span); cost = model call (the most $); quality = the hallucination flag on the output. None would appear in traditional monitoring — it's a 200 OK with acceptable latency. (3) Tail-based — it keeps every interesting span (hallucination-flagged, over-budget, thumbs-down) and samples the boring rest; random 3% would miss most of the failures you care about. (4) Online evals running on sampled live traffic + drift detection segmented by prompt version — they score quality continuously and flag the regression as it happens, since errors/latency are unchanged. (5) Turn it into an eval test case — add that trace to your evaluation dataset so every future deploy is tested against this real failure. That's the observability → eval loop.
Mental-Model Corrections
- "It returns 200 OK, so it's working." A
200means it didn't crash — not that the answer was right, grounded, or on-budget. LLM observability watches quality and cost, not just status. - "APM is enough." APM (latency/errors/uptime) is necessary but not sufficient — it can't see hallucinations, silent quality drift, or token-cost blowups. Add quality, cost, and safety signals.
- "Metrics are the main thing." Metrics tell you something changed; logs let you read it; traces tell you exactly which span broke. For multi-step apps, the trace is the star.
- "Cost is one number per request." The 2026 standard is cost per span — so you can see which step (usually a bloated model call) is the driver.
- "Sample traffic randomly." Use tail-based sampling — keep the interesting spans (flagged, over-budget, thumbs-down), sample the rest. Random sampling misses the failures.
- "Observability is just dashboards." Its highest value is the loop: production traces become eval datasets, so every real failure becomes a permanent regression test.
- "Add observability at the end." Integrate it from day one — it's the layer that tells you which other layer to add next.
Key Takeaways
- Observability is the eyes of the architecture — and LLM observability asks a different question than APM: not "is it up and fast?" but "is it right, safe, and cheap?" A clean
200 OKcan hide a hallucination, a cost blowup, or a silent quality regression. - Three pillars, LLM-flavored: metrics (quality / cost / safety / system), logs ("log everything" — prompts, completions; read prod data daily), and traces — the centerpiece.
- The trace is the star: one request → a span per pipeline step, each with per-span latency, cost, and quality (30–300 spans; cost-per-span is the 2026 standard). It pinpoints the bottleneck, the cost driver, and the quality failure — a glass box, not a black box. Instrument with OpenTelemetry GenAI (
gen_ai.*). - Watch quality, cost, safety, system — segmented by prompt/model/release; alert on hallucination rate and cost/request, not just latency.
- The observability → eval loop is the payoff: online evals on sampled traffic catch drift; tail-based sampling keeps the interesting spans; and production traces become eval datasets — every real failure becomes a permanent regression test. Tools: LangSmith / Langfuse / Helicone / Arize / Datadog.
- The architecture is complete: a model wrapped in gateway+router (L219) · cache (L220) · orchestration (L221) · guardrails (L222) · observability (L223) — grown one layer at a time, driven by what observability shows you.
- Next — a new section: Designing AI Product Experiences (UX). You've made the system fast, cheap, reliable, and observable; now we make it feel great to use — starting with treating latency as a product feature.