Distributed Tracing skill for Codex
From slow p99 to the exact span responsible. Codex starts from a precise symptom, pulls several slow and normal traces through exemplars, logs or TraceQL, and walks each waterfall for critical path, gaps, N+1 calls, retries, queueing and fan-out. It confirms findings with metrics before blaming a span, repairs broken context propagation at proxies and queues, and sets up tail sampling so errors and slow traces are never sampled away.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- distributed-tracing Installs to ~/.agents/skills/distributed-tracing/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install distributed-tracing The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project distributed-tracing Installs to .agents/skills/distributed-tracing/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/distributed-tracing.zipunzip -o distributed-tracing.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) distributed-tracing PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $distributed-tracing, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Debugs latency and errors across microservices using distributed traces - finding the right trace via exemplars, logs or TraceQL, reading waterfalls for critical path, fan-out, N+1 calls, retries, queueing gaps and clock skew, fixing broken context propagation, and configuring tail-based sampling so the interesting traces are kept. Works with Tempo, Jaeger, Zipkin, Honeycomb, Datadog and other OTel backends. Use when a request is slow or failing somewhere across services, when traces are fragmented, or when the traces you need were sampled away.
Use it when
- A request is slow or failing somewhere across several services
- Traces are fragmented into separate roots at service or queue boundaries
- The traces you need were dropped by head sampling
- You need evidence for which dependency causes latency
Use something else when
- Services have no tracing yet: use opentelemetry
- The slow part is inside one process and needs a CPU profile: use flamegraphs
How to check the result
Rerun the same trace query over an equal window after the fix and compare span duration distributions, and confirm the endpoint's latency metric moved, not just one trace.
The mistake it prevents
Drawing conclusions from a single trace, or counting sampled traces to estimate error rates instead of using metrics.
Done well means: you go from a symptom (slow p99, error spike, a customer’s request ID) to the specific span and code path responsible, with evidence from several traces, not one anecdote; and the tracing setup keeps the traces you will need next time.
Inspect first
- Read
AGENTS.md; identify the tracing backend and how to query it (Grafana Tempo with TraceQL, Jaeger UI/API, vendor UI). Querying needs network access to the backend. - Confirm propagation format (W3C
traceparentis the default) and sampling setup: head ratio in SDKs, tail sampling in a Collector, or vendor-side. - Get the symptom precisely: which endpoint, which time window, which percentile, which customers or regions. “Checkout is slow” is not enough.
Find the right traces
| Starting point | How to get traces |
|---|---|
| Latency spike on a metric graph | Exemplars on the histogram panel (Grafana) link to trace IDs from that bucket |
| Error log line | trace_id field in the log; open it directly |
| Customer report with a request ID | Search logs for the ID, take trace_id |
| General slowness | Query the backend for slow traces of that endpoint |
TraceQL (Tempo) examples:
{ resource.service.name = "checkout" && span.http.route = "/api/orders" && duration > 2s }
{ resource.service.name = "checkout" && status = error }
{ span.db.system.name = "postgresql" && duration > 500ms } | count() > 10
{ resource.service.name = "api-gateway" } >> { resource.service.name = "inventory" && status = error }
The >> operator selects descendants, so the last query finds gateway requests where
inventory failed somewhere underneath. Pick at least five slow traces and five normal
ones for the same endpoint; comparing them is the method.
Reading a waterfall
Work through these questions in order:
- Critical path. Which chain of spans determines the end time? Shortening a span off the critical path changes nothing.
- Gaps. Time inside a parent with no child span means uninstrumented work: CPU, lock waits, connection pool waits, GC, or a missing instrumentation library. Add a span or profile there.
- Sequential calls that could be parallel. A staircase of sibling spans to independent services.
- N+1. Dozens or hundreds of identical short DB or HTTP spans under one parent.
See
$n-plus-one-queries. - Retries and timeouts. Repeated client spans to the same target, often ending at a round number (1 s, 5 s, 30 s) that matches a configured timeout.
- Queueing. A large gap between a producer span and the consumer span (linked, not parented) means backlog, not slow processing.
- Fan-out amplification. One request causing calls to many shards or services, where the slowest one dominates (tail at scale).
- Errors. The deepest span with error status is usually the origin; parents just propagate it. Check whether the error was retried and recovered.
Compare slow and fast traces side by side: which span grows? If every span is
proportionally slower, suspect the host (CPU throttling, noisy neighbour, GC) rather
than any one dependency; check $kubernetes-observability metrics for that node.
Clock skew and odd shapes
- A child span that starts before its parent or ends after it means clocks differ between hosts; durations within one span are still reliable, cross-host offsets are not.
- Very short client spans with a long server span (or the reverse) suggest the server is timing something other than the request, or a proxy is buffering.
- Missing root span (trace starts at an internal service) means the edge proxy or gateway is not instrumented or not propagating.
Fixing broken propagation
Symptoms: traces end at a service boundary, the downstream service starts new root traces, or async work is detached.
| Boundary | Typical cause | Fix |
|---|---|---|
| HTTP client | Custom client not instrumented | Use the instrumented client or inject traceparent manually |
| Reverse proxy, API gateway | Strips unknown headers or starts its own trace format | Allow traceparent/tracestate; enable OTel in nginx/Envoy/the gateway |
| Message queue | Context not in message headers/attributes | Inject on publish, extract on consume, link spans |
| Thread pools, executors, goroutines | Context not passed | Pass ctx (Go), wrap executors (Java agent does most), use context.with() (Node) |
| Mixed formats | One side B3, other side W3C | Configure both propagators during migration |
Test propagation with one request and check that every service shows the same trace ID
and correct parent IDs; see $opentelemetry for SDK setup.
Keep the traces you need: tail sampling
Head sampling at 1-10% discards most rare errors. A Collector tier with tail sampling keeps all errors and slow traces plus a baseline:
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100000
policies:
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow
type: latency
latency: { threshold_ms: 2000 }
- name: baseline
type: probabilistic
probabilistic: { sampling_percentage: 5 }
All spans of a trace must reach the same Collector instance: put a first tier using the
loadbalancing exporter with routing_key: traceID in front of the tail-sampling tier.
Size decision_wait above your longest normal trace duration and num_traces for
traces per second times decision wait, with headroom; watch the Collector’s dropped
spans metrics.
Workflow
- Pin down the symptom: endpoint, window, percentile, segment
- Collect 5+ slow or failing traces and 5 normal ones
- Walk the waterfall questions; note the span and pattern that differs
- Confirm with metrics (is this span’s latency up across all traffic?)
- Confirm with code or logs at that span; form one hypothesis
- Fix, deploy, compare the same trace query before and after
- Fix propagation or sampling gaps discovered along the way
Verification
After the fix, rerun the same TraceQL or search for the same window length and compare span duration distributions, and check the RED metrics for the endpoint. One fast trace is not proof.
Output
## Trace investigation: <symptom>
- Window and scope: <time, endpoint, percentile>
- Traces examined: <ids, slow vs normal>
- Finding: <span / pattern> on the critical path accounts for <x of y ms>
- Evidence: <metric or log confirming beyond the sampled traces>
- Cause: <code path, config, dependency>
- Fix and result: <change, before/after numbers>
- Instrumentation gaps found: <propagation breaks, missing spans, sampling>
Gotchas
- Sampled traces are biased to whatever the sampler keeps; do not infer rates or percentages from trace counts. Use metrics for “how often”.
- Auto-instrumentation creates spans for libraries it knows; a slow custom TCP client appears as a gap, not a span.
- Span durations from the browser include network and queuing on the client device; compare with the server span before blaming the backend.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Distributed Tracing"
short_description: "From slow p99 to the exact span responsible"
default_prompt: "$distributed-tracing Checkout p99 jumped to 4 seconds yesterday, use our Tempo traces to find out why" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.