--- 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. ```rust crate::DEBUG_TUI_MOD.then(|| { // ... tracing call ... }); ``` ## 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). ```rust crate::DEBUG_TUI_MOD.then(|| { // % is Display, ? is Debug. 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). ```rust crate::DEBUG_TUI_MOD.then(|| { // % is Display, ? is Debug. 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):** ```rust tracing::debug!("Received input event: {:?}", input_event); ``` **Good (Structured and Gated):** ```rust crate::DEBUG_TUI_PTY_MUX.then(|| { // % is Display, ? is Debug. tracing::debug! { message = "PTYMux::run_event_loop", input_event = %format!("{:?}", input_event) }; }); ```