rust-tracing
Skill0 starsChanged 8 months agoMITView on GitHub
Rust observability with tracing — structured logging, spans, subscribers, and instrumentation. Use when choosing between tracing and log, setting up subscribers and layers, adding #[instrument] to functions, configuring RUST_LOG, instrumenting async code (.instrument() vs span.enter()), integrating tower-http or sqlx middleware, protecting sensitive data in logs, or preparing tracing for production with OpenTelemetry. Also use when reviewing code for logging quality or adding request correlation IDs.
In this repository16 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- rust-apiSkill
- rust-architectureSkill
- rust-asyncSkill
- rust-ci-deploySkill
- rust-databaseSkill
- rust-dddSkill
- rust-errorsSkill
- rust-macrosSkill
- rust-ownershipSkill
- rust-perfSkill
- rust-qualitySkill
- rust-serdeSkill
- rust-testsSkill
- rust-typesSkill
- rust-unsafeSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 64
Never use span.enter() in async code.
- Very specific: line 118
If you would wake someone at 3am for it, it is error.
- Very specific: line 119
If you would mention it in a standup, it is info.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Tracing & Observability
Core Question
What happened, and why, at every layer of this request?
Events say what happened. Spans say the context in
which it happened. If you only emit events without spans,
you lose the "why." tracing unifies both into one
structured, async-aware API.
Error → Design Question
| Symptom | Don't Just Say | Ask Instead |
| Logs are noise, nobody reads them | "Add more log levels" | What decisions will an operator make from this output? |
span.enter() in async code | "Use .instrument()" | Does this span need to cross await points? |
| Sensitive data appears in logs | "Filter it in the output" | Should this field exist in the span at all? |
| No context in error logs | "Add more fields" | Should this be a span (context) rather than an event (fact)? |
info! scattered everywhere | "Logging is good practice" | Which layer owns this observability concern? |
| Perf regression after adding spans | "Remove some traces" | Are you using compile-time filtering and non-blocking writers? |
Quick Decisions
| Situation | Reach For | Why |
| New project, choosing a logging crate | tracing | Structured, span-aware, async-compatible, log-compat |
| Library crate | tracing with log feature | Consumers using log still see events |
| Dev/local output | fmt::layer().pretty() | Human-readable, colored |
| Production JSON output | fmt::layer().json() | Machine-parseable for log aggregators |
| Controlling verbosity at runtime | EnvFilter + RUST_LOG | Per-module filtering without recompile |
| Stripping debug/trace from release | tracing max_level_info feature | Zero cost for disabled levels at compile time |
| Instrumenting an async function | #[instrument] | Auto-creates span, captures args, async-safe |
Propagating span through .await | .instrument(span) | Span stays active across yield points |
| Entering a span in sync code | let _guard = span.enter() | RAII guard, dropped at scope end |
| Axum/tower HTTP request tracing | tower_http::trace::TraceLayer | Per-request spans with method, URI, status, latency |
| SQL query tracing | sqlx with tracing feature | Automatic spans for every query |
| Outbound HTTP tracing | reqwest-tracing | Span per outbound request |
| Protecting sensitive fields | secrecy::Secret<T> or Redacted | Prevents accidental exposure in logs |
| Request ID / correlation | Extract or generate UUID in span field | Ties all spans/events for one request |
| Sending traces to OTel collector | tracing-opentelemetry layer | Bridges tracing spans to Jaeger/Tempo/Datadog |
| Writing traces to files | tracing-appender + non_blocking | Rolling files without blocking the runtime |
| Testing that traces were emitted | tracing-test + #[traced_test] | Captures logs per-test with logs_contain! |
The Async Span Problem
Never use span.enter() in async code. The guard is
tied to the current thread — when the future yields at
an .await and resumes on a different thread, the span
context is wrong.
// Bad: guard held across await
async fn handle(req: Request) -> Response {
let span = info_span!("handle_request");
let _guard = span.enter();
let data = fetch_data().await; // span context lost here
process(data)
}
// Good: #[instrument] handles async spans correctly
#[instrument(skip(req))]
async fn handle(req: Request) -> Response {
let data = fetch_data().await;
process(data)
}
// Good: .instrument() for dynamic span names
async fn handle(req: Request) -> Response {
let span = info_span!("handle_request", method = %req.method());
async {
let data = fetch_data().await;
process(data)
}
.instrument(span)
.await
}Spawned tasks also need explicit span propagation:
let span = info_span!("background_job", job_id = %id);
tokio::spawn(
async move { run_job().await }.instrument(span)
);Log Levels
| Level | Use For | Example |
error | Failures requiring operator attention | DB connection lost, payment failed |
warn | Degraded but recoverable | Retry succeeded, cache miss fallback |
info | Business-significant events | Request handled, user created, deployed |
debug | Developer troubleshooting | SQL text, parsed config, cache hit/miss |
trace | Fine-grained flow | Loop iterations, byte-level I/O |
If you would wake someone at 3am for it, it is error.
If you would mention it in a standup, it is info.
Everything else is debug or trace.
Usage Scenarios
Scenario 1: "I'm starting a new Axum service and need logging"
→ Set up tracing_subscriber with fmt::layer() for dev
and json() for production. Add TraceLayer to your router
for per-request spans. Use #[instrument] on handlers.
See references/setup.md for init patterns.
Scenario 2: "I have logs but can't tell which belong to the same request"
→ Extract or generate a request ID in middleware, add it as
a span field. All events within that span tree carry the ID.
See references/instrumentation.md for correlation patterns.
Scenario 3: "I need traces in Jaeger for production but plain text locally"
→ Use a layered subscriber: fmt::layer() for local,
tracing-opentelemetry for production. Switch via config.
See references/production.md for OpenTelemetry setup.
Reference Files
| File | Read When |
| references/setup.md | Cargo.toml deps, subscriber init (dev/prod), RUST_LOG syntax, EnvFilter, compile-time filtering |
| references/instrumentation.md | #[instrument] options, tower-http/sqlx/reqwest middleware, correlation IDs, what to log by layer |
| references/structured-fields.md | Field sigils (%, ?), naming conventions, error logging patterns, sensitive data protection |
| references/production.md | OpenTelemetry, file output, non-blocking writers, performance, testing, production checklist |
Cross-References
| When | Check |
Span context across .await, Send bounds | rust-async → Quick Decisions |
| Error logging, error chain display | rust-errors → Quick Decisions |
| Project Cargo.toml setup, lint defaults | rust-quality → Quick Decisions |
| Axum middleware and architecture patterns | rust-architecture → Quick Decisions |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

