| name | write-structured-tracing |
| description | Standard for writing structured tracing logs behind debug flags. |
Write Structured Tracing
This skill documents the project's standard for writing tracing logs (tracing::info!, tracing::debug!, etc.). It ensures logs are easily filterable, consistently formatted, and don't affect performance when disabled.
1. Gating Behind Debug Flags
All tracing::*! calls must be gated behind a specific debug flag from tui/src/tui/mod.rs (or similar location) using the .then(|| { ... }) pattern. This ensures the tracing macro and any string allocations are completely bypassed when the flag is disabled.
Choosing the right flag (Scope Specificity):
Make sure the flag you use is specifically scoped to the module or subsystem you are debugging. This scope can be narrow or broad depending on the requirements.
- Do NOT reuse a broad orchestrator flag (e.g.,
DEBUG_TUI_PTY_MUX) for a microscopic, high-volume subsystem (like a byte-stream parser). Doing so would spam the orchestrator logs.
- If a suitable module-specific flag does not exist, create a new one (e.g.,
DEBUG_TUI_VT100_PARSER) to ensure developers can isolate and filter logs effectively without overwhelming the IO overhead.
crate::DEBUG_TUI_MOD.then(|| {
});
2. The // % is Display, ? is Debug. Comment
You MUST add the exact line comment // % is Display, ? is Debug. directly above every tracing::*! invocation. This serves as a quick syntax reminder.
3. Structured Fields and message
Do not use unstructured string formatting (e.g., tracing::info!("Hello {}", name)). Instead, use structured fields with an explicit message key that identifies the context (e.g., the struct and method name).
crate::DEBUG_TUI_MOD.then(|| {
tracing::info! {
message = "ComponentName::method_name",
status = "Something happened",
};
});
4. Using inline_string! vs format! for Complex Formatting
When you need to format complex strings within a tracing field, bind it to a field using the % (Display) modifier and follow this rule:
- If the resulting string will be <= 16 bytes, use the
inline_string! macro to stack-allocate it.
- If the resulting string will be > 16 bytes, use the standard
format! macro (since inline_string! would spill to the heap and allocate anyway).
crate::DEBUG_TUI_MOD.then(|| {
tracing::info! {
message = "AppNoLayout::handle_event",
input_event = %inline_string!(
"{a} {b:?}",
a = glyphs::USER_INPUT_GLYPH,
b = input_event
)
};
});
Example Transformation
Bad (Unstructured and Un-gated):
tracing::debug!("Received input event: {:?}", input_event);
Good (Structured and Gated):
crate::DEBUG_TUI_PTY_MUX.then(|| {
tracing::debug! {
message = "PTYMux::run_event_loop",
input_event = %format!("{:?}", input_event)
};
});