Deploy & Ops
Use when adding lightweight per-request observability to an API, especially an LLM or media route, so you can see where the time went, which model ran, and what the request cost - without a heavyweight tracing vendor. Also use when a route is intermittently slow and nothing records which step was responsible. Covers timed spans, one durable row per request, and best-effort writes that never throw into the caller. If latency or cost needs attributing, use this skill.
1 file
Description
Use when adding lightweight per-request observability to an API, especially an LLM or media route, so you can see where the time went, which model ran, and what the request cost - without a heavyweight tracing vendor. Also use when a route is intermittently slow and nothing records which step was responsible. Covers timed spans, one durable row per request, and best-effort writes that never throw into the caller. If latency or cost needs attributing, use this skill.
A route does several timed steps (auth, a model call, a storage write) and you want a durable record of each step's duration, the model used, and the credits spent, grouped by conversation so a UI can chart per-thread timelines.
Construct a tracer at the start, time named sub-steps with span(), record the model and credits as you go, then write exactly one row on end(). A span stores its start offset from the request start plus its duration, which is all a waterfall needs.
const t = startTrace({ clerkId, threadId, route, method });
const reply = await t.span("model", () => streamText({ model, messages }));
t.setModel(model);
t.addCredits(cost);
await t.end(); // writes one request_traces row
The span helper records the span whether the inner function resolves or throws (record in finally, then rethrow), so a failed step still shows up in the waterfall.
Every write is best-effort: a failed insert is swallowed, never thrown into the caller. Tracing that can crash the request it measures is worse than no tracing. Wrap all persistence so the worst case is a missing row, not a 500.
threadId (the logical conversation or session) on every trace so the UI can roll up per-thread stats and render a timeline.kind derived from the route prefix (voice, image, upload, chat, other) so daily activity charts by family.finally.Added 2026-07-01. Back to the Skill Library.

New tutorials, open-source projects, and deep dives on coding agents - delivered weekly.