Skip to content

Routing and reliabilityGuide

LLM observability: what to measure in production

Logs are not observability. Production AI needs a record per request that ties together route, attempts, latency, tokens, estimated cost and outcome — and a privacy posture that does not depend on reading prompts.

By INFRO Editorial

Published

Updated

11 min read

A dark operational observatory showing glowing traces, latency signals, and cost telemetry around AI requests

In short

LLM observability starts with one complete record per request: what was asked for and what served it, every failed attempt, time to first token and total duration, token counts, estimated cost and the outcome. Build the four golden signals on top of those records, add quality signals your application can compute without reading content, and keep prompt capture off unless a specific investigation needs it.

Key takeaways

  • The unit of LLM observability is the request record, not the log line: model requested and served, attempts, timing, tokens, cost and outcome together.
  • A 200 status hides most LLM failures. Track malformed output, tool-call failures and fallbacks as first-class errors.
  • Record the model the provider says answered, not only the one you asked for; a new snapshot behind an old name is a change worth an alert.
  • Measure time to first token separately from total duration; averages hide the stalls users feel.
  • Treat estimated cost as telemetry, recorded per request at the rate your account actually pays, with unknown rates recorded as unknown.
  • Metadata answers most operational questions. Capture prompts only deliberately, with a retention window and access controls.

Teams often discover the limits of their AI monitoring the same way. A dashboard says error rates are flat and latency is normal, while support tickets say the assistant is producing nonsense, a batch job's bill has doubled, or answers take twenty seconds to start. Each of those is a failure an ordinary API dashboard cannot see: the status code was 200, the average latency was fine, and cost was not a metric at all.

This guide describes what to record for every LLM request, the signals to build on those records, how to connect them to the rest of your tracing, and how to do it without storing people's prompts.

Why API monitoring misses LLM failures

Four properties of LLM traffic break the assumptions a generic HTTP dashboard makes:

  • Success is not correctness. A response can arrive with status 200 and still fail its purpose: invalid JSON, a hallucinated tool argument, a refusal, an answer from a fallback model that was never evaluated for the task.
  • Latency has two shapes. For streamed responses, the time until the first token decides whether a product feels responsive; the total duration decides throughput. One number cannot represent both.
  • Cost varies per request. Two requests to the same endpoint can differ in cost by orders of magnitude, driven by prompt length, output length, cache hits and which account served them.
  • One request can be several attempts, and several calls. When a gateway fails over, the successful response hides the failed attempts that preceded it — and their latency and, sometimes, their cost. A request can also trigger calls of its own: a classifier that picks the route, a safety check on the prompt, a background evaluation of the answer. Each costs money and can fail, and none of them is the request the user made.

Adapt the four golden signals

Google's SRE guidance names four golden signals for monitoring a user-facing system: latency, traffic, errors and saturation1. Each needs an LLM-specific definition.

SignalFor LLM traffic, measureWhy the plain version is not enough
LatencyTime to first token and total duration, by model and target, as percentilesAverages hide stalls; total duration hides slow starts
TrafficRequests and tokens, input and output separately, by workloadRequest counts do not predict cost or rate-limit pressure
ErrorsError statuses plus malformed output, tool-call failures, refusals and fallbacksMost harmful failures return 200
SaturationRate-limit responses per account and budget consumption per scopeThe constraint is usually an allowance, not a CPU
The golden signals, redefined for model traffic.

The request record is the unit of observability

A log line per request is easy to produce and hard to use. What operators need is one structured record per request that answers "what happened, why, and what did it cost" without joining three systems.

Identity

  • id (also the X-INFRO-Request-Id header)
  • project, key
  • metadata.user_id, session_id, tags

Path

  • model_requested → model
  • route_slug, provider, connection
  • fallback_attempts[]

Outcome

  • http_status
  • error.type, error.code, error.message

Timing

  • latency_ms
  • first_token_ms
  • spans: gateway, provider, stream

Usage and cost

  • prompt / completion tokens or units
  • usage.cost (estimate) and price_source
  • cost_breakdown
The fields a request record needs, grouped by the question each group answers. The field names shown are the ones INFRO uses; the grouping applies to any gateway.
Field groupContentsThe question it answers
IdentityA request id returned to the caller, project, key, and your own metadata such as user, session or featureWhose request was this, and where did it come from?
PathModel requested, model served, route, provider, account, and each failed attempt with its error and latencyWhere did it run, and did it recover from anything?
OutcomeHTTP status and the error type, code and message exactly as returnedDid the caller get what they needed?
TimingTime to first token, total duration, and the upstream portionWas it slow, and was the slowness ours or the provider's?
Usage and costInput and output tokens (or media units), estimated cost and where the rate came fromWhat did it consume, and what will the provider bill for it?
A request record that can answer an incident question on its own.

Two fields deserve emphasis. Model requested versus model served is how you detect a silent substitution — an alias that resolved differently, or a fallback that answered with another model. Record a third value too, the model id the provider's response names, because providers move a model name to a new snapshot without changing the name you send. The request id must be returned to the caller, so a support ticket, an application log and the gateway record can all be joined on one value.

Calls a request triggers belong in the same store, as rows of their own linked to the request they served, with their own cost and outcome. Folded into the parent, a classifier's timeouts and its share of spend disappear; the argument is made at length in classifiers in the request path.

Connect LLM records to the rest of your traces

An LLM call is usually one span inside a larger request. W3C Trace Context standardizes the traceparent and tracestate headers that carry trace identity between services2, so a gateway that accepts and propagates them lets a model call appear inside the trace of the user action that caused it.

OpenTelemetry is developing semantic conventions for generative AI that name the attributes such spans should carry — for example gen_ai.request.model, gen_ai.response.model, gen_ai.provider.name, gen_ai.usage.input_tokens and gen_ai.usage.output_tokens3. The conventions are marked as in development, so expect names to change and pin the version your collector expects.

Treat cost as a telemetry signal

Model responses report usage: the Chat Completions response includes prompt and completion token counts, with cached prompt tokens broken out when prompt caching applied4. Anthropic reports cache writes and cache reads as separate fields, and prices them differently from ordinary input tokens5. Turning those counts into cost at the time of the request — rather than at the end of the month — makes cost a signal you can alert on and attribute.

Three rules keep it trustworthy:

  1. Use the rate your account pays. A negotiated rate, or a cloud platform's rate card, can differ from the model vendor's list price. Record which rate was used.
  2. Record unknown as unknown. A model with no rate on file should produce an empty cost, not zero. Zero reads as free and quietly understates spend.
  3. Keep the estimate separate from the invoice. A per-request estimate is what budgets and attribution are computed from; the provider's invoice is the authority. Compare them periodically and investigate a sustained gap.

If anything in front of the provider answers requests itself — an exact-match response cache, for instance — mark those responses. A cache hit costs the provider nothing and returns in milliseconds; mixed into the same series, it flatters both p95 latency and cost per request, and hides a provider getting slower. Report hits against lookups, and their latency apart from provider latency. When to use such a cache at all is covered in exact-match response caching.

Quality signals that do not require reading prompts

Most quality regressions can be detected from signals the application computes anyway:

  • the share of responses that fail schema validation or parsing;
  • tool calls with invalid arguments, and tools that returned errors;
  • refusals and truncated outputs (a finish reason of length rather than stop);
  • retries triggered by the application itself;
  • user corrections, regenerations and escalations to a person;
  • scores from a sampled, access-controlled evaluation run.

Record these as metadata on the request or as events joined by request id. They catch the release that made a prompt worse without anyone reading a customer's conversation.

Changes that arrive with status 200

Some regressions are not caused by your release at all. A provider moves a model name to a new snapshot and answers get longer. A small edit near the top of a prompt stops the provider's prompt cache from matching, and input cost rises. A primary starts timing out for a few requests, and they quietly move to a fallback with a higher rate. None of these produces an error, and each is visible in metadata alone:

SignalWhat usually caused it
A served model id not seen in recent weeksThe provider moved the model name to a new snapshot
Output tokens per request risingA prompt or parameter change, or a new snapshot
Cached share of prompt tokens fallingThe start of the prompt changed
Share of requests served by a fallback risingThe primary is failing or slow for some requests
Share of answers stopped by the length limit risingAnswers are reaching max_tokens
Silent changes detectable from request records, without reading content.

Compare each against its own recent baseline, per model and per account, and require both a relative and an absolute change before alerting, or every quiet night becomes a finding. What metadata cannot tell you is whether answers got worse; that needs an evaluation, as described in how to evaluate LLMs on real workloads.

Metrics that should change a decision

A metric earns a place on a dashboard when crossing a threshold triggers an action someone owns.

MetricAction when it moves
Fallback rate per accountInvestigate the account; consider taking it out of rotation
p95 time to first token per model and targetCheck the target's health; lower the per-attempt timeout; move interactive traffic
Malformed-output rate after a releaseRoll back the prompt or model change
Rate-limit responses per accountSpread load across accounts or request a higher allowance
Cost per successful task per workloadRe-evaluate the model choice or reduce context
Unpriced request shareAdd the missing rates so budgets and attribution are complete
New served model id on a connectionRe-run the evaluation for workloads on that model
Each metric paired with the decision it exists to drive.

Privacy, retention and access

Request metadata can reveal a great deal — which customers use a feature, when, and how heavily — and prompts and completions can contain exactly the personal and confidential data that sensitive-information-disclosure risks describe6. Decide the policy before the first incident, when there is time to think:

  • Keep prompt and completion capture off by default. Enable it for a defined purpose, a defined scope and a defined retention window.
  • Keep operational metadata longer than content. Most investigations need the path, timing and error, not the text.
  • Redact secrets before anything is logged, including inside tool arguments, and make sure the redacted text is what every downstream copy sees: retries, captures and exported spans.
  • Apply the same role boundaries to reading request data as to managing production keys.

An annotated request record

The record below is illustrative and abbreviated, in the shape INFRO returns. Each field answers one of the questions above.

GET /v1/requests/{id} (abbreviated)
{
  "id": "req_…",
  "status": "ok",
  "http_status": 200,
  "model_requested": "support-drafts",
  "model": "anthropic/claude-sonnet-5",
  "served_model": "claude-sonnet-5",
  "finish_reason": "stop",
  "route": "primary",
  "route_slug": "support-drafts",
  "provider": "anthropic",
  "connection_name": "Anthropic production",
  "fallback_attempts": [],
  "latency_ms": 4210,
  "first_token_ms": 640,
  "usage": {
    "prompt_tokens": 1830,
    "completion_tokens": 412,
    "total_tokens": 2242,
    "cost": 0.01167,
    "price_source": "list"
  },
  "metadata": { "user_id": "cust_8231", "tags": { "feature": "ticket-reply" } }
}
  • model_requested is a route slug and model is what served it, so a substitution would be visible here.
  • served_model is the id the provider's response named; a new value here, with the same model, is a snapshot change.
  • finish_reason is stop, not length, so the answer was not cut off.
  • fallback_attempts is empty: nothing failed before this response.
  • first_token_ms and latency_ms separate a slow start from a long answer.
  • price_source says the estimate used the vendor's list rate, not a negotiated one.
  • metadata carries attribution you supplied, without any prompt content.

How INFRO records requests

Every request through INFRO produces a record like the one above, including refused and failed requests once the gateway knows which model was asked for. Every response carries an X-INFRO-Request-Id header with the same id. The records are about traffic on your own provider accounts; every cost in them is an estimate of what your provider will bill, null when the model is unpriced, and never includes INFRO's subscription.

  • GET /v1/requests lists records with filters for status, model, project, key, provider, connection, route, and the user_id, session_id and tag.<key> values you send in request metadata; sort can be newest, slowest or costliest. GET /v1/requests/{id} returns one record with its timing spans.
  • GET /v1/usage aggregates requests, units, estimated cost, unpriced requests, error rate and p50, p95 and p99 latency by hour, day or month, grouped by model, modality, project, key, endpoint, provider, connection or route. Latency is measured on calls that reached your provider.
  • Calls INFRO makes for a request — a guardrail classifier check, a route's prompt classifier, an eval monitor's score — are rows of their own with their own estimated cost, carrying purpose and parent_request. Answers from INFRO's opt-in response cache carry a cache object and provider_billed: false, can be filtered with cache=hit, and their latency is reported apart from provider latency.
  • Traffic changes watches the signals in the table above — a model answered by a new snapshot, output tokens, cached share, fallbacks, length-limited answers, p95 latency and time to first token, error codes — and raises each as a finding with the likely cause and the estimated change in provider cost. It reads metadata only, never prompts or answers.
  • Request metadata is kept for a window set by your plan: 7 days on Developer, 14 on Startup, 30 on Team, 90 on Business. Prompt and completion capture is off by default; when an organization enables it, payloads are kept for a retention window it chooses, within its plan's limit. With guardrails (Business plan), a redacted prompt is what failover targets, capture and the OpenTelemetry export all see, and findings are counts per detector, never the matched text.
  • From the Team plan, spans can be pushed to your own OpenTelemetry collector over OTLP/HTTP, nested in your trace when the request carries a W3C traceparent. Spans carry INFRO's own infro.* attributes and the core generative AI ones — gen_ai.operation.name, gen_ai.request.model, gen_ai.usage.input_tokens and gen_ai.usage.output_tokens, plus gen_ai.provider.name where the convention defines a value for that provider. Prompt and response captures are never exported.

For the people who will never open a request log, the console's Summary turns the same records into spend against budget and four status lights, with a weekly and monthly email; the format is described in the AI spend report leadership reads. See request traces, analytics and exports and OpenTelemetry for the full contract.

From the team that builds it

Where this shows up in INFRO

INFRO is a control plane in front of the provider accounts you already hold. The capabilities below are the parts of it this article touches, each with the documentation that defines its behaviour.

  • Request traces and payload logs

    Available with a plan

    Per-request timing detail, with prompt and completion logging off by default and a retention window you choose.

    Read the documentation for Request traces and payload logs
  • Usage and cost per request

    Available with a plan

    Every request records its model, latency, token or unit counts, and estimated provider cost, queryable by project and key.

    Read the documentation for Usage and cost per request
  • Spend attribution

    Available with a plan

    Group cost by model, project, key, or an end-user id you attach to the request.

    Read the documentation for Spend attribution
  • Usage, request and FOCUS exports

    Available with a plan

    GET /v1/usage and GET /v1/requests on any gateway key, the request log and usage series as CSV, plus a FOCUS v1.4 Cost and Usage dataset as JSON or CSV, so AI spend lands in your FinOps platform beside the rest of your cloud cost.

    Read the documentation for Usage, request and FOCUS exports
  • OpenTelemetry export

    Available with a plan

    Spans pushed to your own collector from the request path, with the endpoint and headers set in the console. Included from the Team plan.

    Read the documentation for OpenTelemetry export
  • Traffic changes

    Available with a plan

    Changes in your traffic that come with no error: a model answered by a new snapshot, longer answers, a lost prompt cache, more fallbacks, answers cut off by the length limit, slower responses or a new error mix. Each finding names the likely cause and the estimated weekly change in provider cost. INFRO sees request metadata, not answers, so it does not judge answer quality.

    Read the documentation for Traffic changes
  • Response cache

    Available with a plan

    Opt-in, exact-match cache for chat completions. An identical request inside the time-to-live you set is answered from the cache and never reaches your provider, so your provider does not bill it. Every plan; the longest time-to-live depends on the plan. No semantic matching.

    Read the documentation for Response cache
  • Guardrails

    Available with a plan

    Pattern-based checks on chat completion prompts and responses: block, redact or flag email addresses, phone numbers, card numbers, IBANs, US Social Security numbers, IP addresses, API keys and your own terms, plus optional classifier checks on your own connection to a decision model's provider. Detection can miss things. Included from the Business plan.

    Read the documentation for Guardrails

INFRO accounts are self-serve. Provider usage is billed to you by your providers; INFRO charges a software subscription and takes 0% of model spend. See plans.

Sources

Numbered in order of first citation. Each was opened and checked against the claim on the date shown.

  1. 1.Monitoring Distributed Systems — Site Reliability Engineering (opens in a new tab) — Google. Accessed Oct 13, 2026.
  2. 2.Trace Context (W3C Recommendation) (opens in a new tab) — W3C. Accessed Oct 13, 2026.
  3. 3.OpenTelemetry semantic conventions for generative AI (opens in a new tab) — OpenTelemetry. Accessed Oct 13, 2026.Span attributes are defined in docs/gen-ai/gen-ai-spans.md; status Development on 2026-10-13.
  4. 4.Create chat completion — API reference (opens in a new tab) — OpenAI. Accessed Oct 13, 2026.
  5. 5.Prompt caching (opens in a new tab) — Anthropic. Accessed Oct 13, 2026.
  6. 6.OWASP Top 10 for LLM Applications 2025 (opens in a new tab) — OWASP GenAI Security Project. Accessed Oct 13, 2026.

About this article

Written from the request-logging and export implementation and public documentation of the INFRO gateway, which the authors build, and from published monitoring practice and specifications. Sources were re-opened on 13 October 2026. External claims link to their sources. The request record shown is illustrative.

Last checked against its sources on . The next review is due by . No separate named reviewer is recorded for this article.

How articles are written, sourced and corrected is set out in the editorial standards.

Revision history

  1. Reviewed against every source. Added detection of changes that return 200 (a new model snapshot behind an old name, longer answers, a falling cached share), recording the extra calls a request triggers — classifier checks, evaluations — as rows of their own, and keeping response-cache hits apart from provider latency and cost. Corrected the INFRO section: exported spans now carry the core OpenTelemetry generative AI attributes alongside INFRO's own, and it now covers traffic-change findings, guardrail redaction before capture and export, and the leadership Summary. Updated the OpenAI API reference link, which had moved.
  2. Expanded from a short overview into a full guide: the request record as the unit of observability, golden signals adapted to LLM traffic, tracing and OpenTelemetry conventions, cost as telemetry, quality signals without content, privacy and retention, and an annotated record. Added cited sources.