Production AI Guide

AI Observability: Logging, Tracing, and Monitoring AI Features in Production

Your APM dashboard shows a 200 and 800ms latency. It doesn't show which model answered, what the prompt actually said, how many tokens it cost, or why the agent looped three times before returning. Generic observability wasn't built for AI calls. This guide covers what to log, how to trace a multi-step AI pipeline, which metrics actually predict incidents, and how to build a dashboard that answers "why did the AI do that" instead of just "was the request slow."

Last reviewed: Jul 15 2026

Isometric diagram of glowing server nodes connected by cyan trace lines converging into a central monitoring console, with a few nodes lit in warm amber.
One trace ID, many spans — turning a scattered pipeline into a single story you can follow.

What Generic APM Misses

Standard application monitoring — request duration, status codes, error rates — treats an AI call like any other HTTP request. That's not wrong, but it's incomplete. An AI request has dimensions a normal API call doesn't:

None of this shows up in a standard trace unless you put it there. AI Cost Modeling covers estimating spend before you ship; this guide covers watching it — and everything else — after you ship.

The Question Observability Should Answer

When someone reports "the AI gave a weird answer" or "this feature is slow," you should be able to reconstruct the exact prompt, model, tool calls, and token usage for that specific request — without asking the user to reproduce it.


Structured Logging: What to Capture

Log AI calls as structured events, not free-text lines. A single JSON object per call, written to whatever log pipeline you already use, is enough to answer most incident questions:

{
  "event": "ai_call",
  "request_id": "req_8f2c1a",
  "timestamp": "2026-07-15T14:22:03Z",
  "model": "claude-sonnet-4-6",
  "prompt_version": "support-reply-v3",
  "input_tokens": 842,
  "output_tokens": 213,
  "cache_read_tokens": 620,
  "latency_ms": 1140,
  "tool_calls": ["lookup_order"],
  "status": "success",
  "user_id_hash": "u_9a31...",
  "stop_reason": "end_turn"
}

Note what's not in this event: the raw prompt text and the raw completion text. Logging full prompt/response content is the single most common way AI observability turns into a data-leak incident — customer PII, credentials pasted into a support ticket, or business logic embedded in a system prompt all end up sitting in your log aggregator with looser access controls than your database.

Log Metadata by Default, Content by Exception

Log token counts, model, latency, and status unconditionally. Log the actual prompt/completion text only behind a feature flag, for a sampled percentage of traffic, with the same redaction rules described in Sanitizing Code and Data Before Sending to AI — strip credentials, PII, and full customer records before they reach a log line, not after.

Correlating Logs with a Request ID

Generate one request ID at the edge of your system and thread it through every AI call, tool invocation, and retry that request triggers. Without it, a single user action that fans out into three AI calls and two tool calls produces five unrelated log lines with no way to reconstruct the sequence.

async function handleSupportReply(ticketId: string, requestId: string) {
  const context = await lookupTicket(ticketId, { requestId });
  const draft = await anthropic.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 1024,
    messages: [{ role: 'user', content: buildPrompt(context) }],
  });

  logAiCall({
    requestId,
    event: 'ai_call',
    model: draft.model,
    inputTokens: draft.usage.input_tokens,
    outputTokens: draft.usage.output_tokens,
    stopReason: draft.stop_reason,
  });

  return draft;
}

Tracing Multi-Step AI Pipelines

A single AI call is easy to log as one event. A multi-agent pipeline or a tool-calling loop is not — one user action can trigger an orchestrator call, three subagent calls, and five tool invocations, each with its own latency and failure mode. This is exactly what distributed tracing is for: one trace, many spans, each span showing where time and tokens actually went.

OpenTelemetry is the standard choice — vendor-neutral, with exporters for Datadog, Honeycomb, Jaeger, and most APM backends. Wrap each AI call and each tool call in its own span, nested under a parent span for the overall request:

import { trace } from '@opentelemetry/api';

const tracer = trace.getTracer('ai-pipeline');

async function runAgentTask(task: string, requestId: string) {
  return tracer.startActiveSpan('agent.orchestrate', async (span) => {
    span.setAttribute('request_id', requestId);

    try {
      const plan = await tracer.startActiveSpan('ai.call.plan', async (planSpan) => {
        const result = await anthropic.messages.create({
          model: 'claude-sonnet-4-6',
          max_tokens: 512,
          messages: [{ role: 'user', content: `Plan: ${task}` }],
        });
        planSpan.setAttribute('input_tokens', result.usage.input_tokens);
        planSpan.setAttribute('output_tokens', result.usage.output_tokens);
        planSpan.end();
        return result;
      });

      for (const step of parseSteps(plan)) {
        await tracer.startActiveSpan(`tool.${step.tool}`, async (toolSpan) => {
          toolSpan.setAttribute('tool_name', step.tool);
          await executeToolCall(step);
          toolSpan.end();
        });
      }

      span.end();
    } catch (err) {
      span.recordException(err as Error);
      span.end();
      throw err;
    }
  });
}

With this in place, a single trace ID shows the full shape of an agent run: how long planning took versus each tool call, which step failed, and how token usage split across the sub-calls. This is the difference between "the agent timed out" and "tool call #4 (inventory lookup) hung for 9 seconds while the model itself responded in 400ms." See Multi-Agent Systems and Tool Use for the orchestration patterns these spans are wrapping.

One Trace ID, Many Spans, No Guessing

The goal isn't more dashboards — it's turning "something in the pipeline was slow" into "step 3 of 5 was slow, here's exactly why," without adding print statements and re-running the request.


Metrics That Predict Incidents

Dashboards drown in metrics that don't change your response to an incident. For AI features, a small set actually matters:

Metric Why it matters Alert on
Latency by model + prompt version A prompt change or model swap can silently double latency p95 latency shift after a deploy
Error rate by error type Rate limits, context-length errors, and tool failures need different fixes Any non-zero rate-limit rate in production
Token cost per request The unit economics metric — see AI Cost Modeling Cost per request above the number you budgeted
Tool call failure rate Silent tool failures produce confidently wrong answers Any spike in retried or failed tool calls
Output quality drift Same prompt, degrading answers over time — a model or data drift signal Eval score drop — see AI Evals in Production

Cost and quality metrics come from two different systems that should feed the same dashboard: AI Cost Modeling covers budgeting and spend alerts, and AI Evals in Production covers running eval suites against production traffic to catch quality regressions. Observability is the layer that correlates both against latency and errors in real time, instead of leaving them as separate weekly reports.


Building a Dashboard for Incident Response

A useful AI dashboard answers three questions in order, because that's the order an on-call engineer actually asks them during an incident:

  1. Is it happening now, and how widely? Error rate and latency, sliced by model and prompt version, over the last hour.
  2. What changed? A timeline of prompt deploys, model version changes, and config changes overlaid on the error/latency graphs — most AI incidents follow a prompt or model change, not a code change.
  3. Which specific requests were affected? A way to jump from "errors spiked at 14:22" to the actual request IDs, traces, and (if sampled) prompts from that window.
Track Prompt and Model Changes Like Deploys

Most observability setups only mark code deploys on the timeline. Add prompt version bumps and model swaps as first-class deploy markers — they change AI behavior at least as often as code does, and they're the first thing to check when a graph moves.


Copy-Paste Prompt: Instrument an Existing AI Integration

Use this to add logging and tracing to an AI call that currently has none:

I have an AI integration in [file/function] that calls [model/provider]
with no observability. Add:

1. A structured log event per call: request ID, model, prompt version,
   input/output/cache token counts, latency, tool calls made, status,
   and stop reason. Do not log raw prompt or completion text by default.
2. An OpenTelemetry span wrapping the call, nested under the current
   request's parent span if one exists, with token counts and model
   as span attributes.
3. If this call is part of a multi-step pipeline (tool calls, subagent
   calls), wrap each step in its own child span so the full pipeline
   shows up as one trace.
4. A feature-flagged path to log the actual prompt/completion text for
   a sampled percentage of requests, using the redaction rules from
   [reference to your data-sanitization approach].

Show me the diff. Do not change the AI call's actual behavior or
retry logic — this is instrumentation only.

AI Observability Checklist

Related Guides

AI Cost Modeling

Token budgets, model selection, prompt caching, and spending alerts — the cost side of what this guide monitors.

AI Evals in Production

Eval datasets, prompt regression in CI, and drift monitoring — the quality side of what this guide monitors.

Back to Home