Claude API Logging & Observability Setup Guide
If you're searching for a logging and observability setup guide for the Claude API, you're probably past the "hello world" stage and running into a real problem: you can't tell why a request failed, how much a feature costs, or which prompt version is live in production. This guide covers exactly what to log, how to structure it, and which tools to use so you get visibility without drowning in noise.
The short answer: capture structured logs at three levels — request/response, token usage, and errors/latency — ship them to a place you can query, and add dashboards for the handful of metrics that actually matter (cost per request, p95 latency, error rate by type, tool call success rate). Below is how to build that from scratch, and what you get for free if you route traffic through SubToAPI instead.
What to log for every Claude API call
Don't log everything indiscriminately — that gets expensive and hard to query. Log these fields for every request:
- Request ID — generate your own UUID even if the provider gives one, so you can correlate logs across services
- Model —
claude-opus-4,claude-sonnet-4, etc. Model mix shifts over time and you want history - Input/output token counts — the single most important field for cost analysis
- Latency — time to first token (for streaming) and total completion time
- Status — success, rate-limited, timeout, tool-use-required, content-filtered
- Prompt/template version — if you template prompts, log which version was used
- User or tenant ID — scoped, not raw PII, so you can slice usage by customer
- Tool calls made — names and count, if you use tool use
Skip logging full prompt and completion text by default in production. It's a privacy liability and a storage cost sink. Log it only behind a sampling flag (e.g., 1% of requests, or on error) for debugging.
A minimal structured log entry
{
"request_id": "a1b2c3d4",
"timestamp": "2025-01-14T10:32:00Z",
"model": "claude-sonnet-4",
"tenant_id": "cust_892",
"input_tokens": 412,
"output_tokens": 180,
"latency_ms": 2140,
"status": "success",
"tool_calls": ["get_weather"],
"prompt_version": "support-reply-v3"
}
Emit this as a single JSON line per request. Line-delimited JSON is easy to ship to any log aggregator and easy to query with jq locally during development.
Instrumenting the API calls
Wrap every Claude API call in a single function so logging is consistent and can't be forgotten in a new code path.
async function callClaude(params) {
const start = Date.now();
const requestId = crypto.randomUUID();
try {
const response = await client.messages.create(params);
logEvent({
request_id: requestId,
model: params.model,
input_tokens: response.usage.input_tokens,
output_tokens: response.usage.output_tokens,
latency_ms: Date.now() - start,
status: "success",
});
return response;
} catch (err) {
logEvent({
request_id: requestId,
model: params.model,
latency_ms: Date.now() - start,
status: err.status === 429 ? "rate_limited" : "error",
error_message: err.message,
});
throw err;
}
}
This single wrapper is where you add tracing spans (OpenTelemetry), metrics counters (Prometheus/StatsD), and log shipping. Every call to Claude should go through it — no exceptions, even for one-off scripts.
Where to send the logs
Pick based on your stack size:
- Small team, early stage: ship JSON lines to a file or stdout, let your container platform (Fly.io, Render, Railway) forward them to its built-in log viewer
- Growing team: send to a dedicated log store — Datadog, Axiom, or a self-hosted Loki stack — so you can run queries like "p95 latency by model, last 24h"
- Need dashboards fast: push usage events to a time-series store (Prometheus + Grafana) for cost and latency graphs, and keep raw JSON logs in object storage for debugging
Whatever you pick, make request_id the join key across systems. If a customer reports an issue, you should be able to go from "it happened around 10:30am" to the exact request in under a minute.
Metrics that actually matter
Don't build a dashboard with forty panels nobody looks at. Track:
- Cost per day/week, broken down by model and by tenant
- p50/p95/p99 latency, separately for streaming time-to-first-token and total completion time
- Error rate by type — rate limits, timeouts, content filtering — each needs a different fix
- Tool call success/failure rate, if you rely on tool use for critical workflows
- Token usage trend per customer, to catch runaway usage before it becomes a billing surprise
Alert on error rate spikes and on any sudden latency increase above baseline — both usually mean something changed upstream or in your prompt, not that "Claude is just slow today."
Getting observability without building the pipeline
Building the above from scratch is a reasonable weekend project, but it's also exactly the kind of infrastructure that's easy to under-invest in and painful to retrofit later. If you'd rather not maintain a logging pipeline, SubToAPI gives you per-key usage metadata — tokens, latency, and request status — out of the box when you call Claude through it.
Setup is the same messages call you already know, just pointed at SubToAPI:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Summarize this ticket"}]
}'
Every call shows up in the dashboard with token counts, latency, and status, scoped by application key — so if you issue a separate key per service or customer, you get per-tenant usage breakdown without writing a single logging line. It won't replace full tracing if you need distributed spans across microservices, but it covers the cost and usage observability most teams actually need day one. See the quickstart and messages docs for setup, or check pricing if you're evaluating it for a team.
FAQ
Do I need to log full prompt and response text? Not by default. Log it only on error or via low-rate sampling (1–5% of traffic). Full-text logging in production is expensive and a privacy risk; token counts and status codes answer most debugging questions.
What's the difference between logging and observability here? Logging is recording discrete events (one line per request). Observability is being able to ask questions you didn't anticipate — latency trends by model, cost spikes by tenant — which requires structured, queryable logs plus dashboards, not just a log file.
Can I get usage metadata without building my own pipeline? Yes — routing calls through SubToAPI gives you per-key token usage, latency, and status in a dashboard immediately, which covers most cost and usage observability needs without custom instrumentation.