Standard application monitoring isn't enough for LLM apps. A 200 OK with a confident-sounding but hallucinated answer looks fine to your metrics stack. LLM observability means tracking the things that tell you whether your AI is actually working — latency distribution, token spend, error and refusal rates, retrieval quality — and wiring them so you can debug, alert, and improve. Here's how to do it.
Why LLM apps need different observability
In a traditional service, a successful response is a successful response. In an LLM app:
- A hallucination returns 200 with a plausible-sounding wrong answer.
- A refusal returns 200 but produces no useful output.
- Latency variance is much wider (1s to 30s+), so percentiles matter more.
- Cost is embedded in the response (
usage.prompt_tokens,usage.completion_tokens) — you need to extract and aggregate it. - Retrieved context in a RAG app directly determines answer quality — if retrieval degrades, so does the model.
How the signals flow
The four signal classes to instrument
1. Latency
Track p50, p95, and p99 per endpoint and model tier. p99 is where user experience breaks.
- Total request latency (end to end)
- Time-to-first-token (for streaming responses — what users feel as "responsiveness")
- LLM call latency vs. retrieval latency vs. your own processing (understand where time goes)
Alert on p95 > your SLA threshold, and on any sudden spike.
2. Token usage and cost
Extract from every response and aggregate by feature, model, and tenant:
usage = response.usage
input_tokens = usage.prompt_tokens
output_tokens = usage.completion_tokens
cost = (input_tokens * INPUT_PRICE) + (output_tokens * OUTPUT_PRICE)
Track: tokens/request (input vs output), cost/request, cumulative cost/day. Alert on unexpected spikes — a prompt change that doubles token usage hits your bill before you notice. See token budgeting for sizing.
3. Error and refusal rates
Hard errors (rate limit, timeout, invalid request) are easy — they throw. Soft errors are subtle: