Skip to main content

tsz_cli/
tracing_config.rs

1//! Tracing configuration for debugging conformance failures.
2//!
3//! Supports three output formats controlled by `TSZ_LOG_FORMAT`:
4//!
5//! - `text` (default): Standard `tracing-subscriber` flat output
6//! - `tree`: Hierarchical indented output via `tracing-tree` — easy to read,
7//!   great for pasting into conversations
8//! - `json`: One JSON object per span/event — machine-readable, also pasteable
9//!
10//! ## Quick start
11//!
12//! ```bash
13//! # Human-readable tree (recommended for debugging conformance)
14//! TSZ_LOG=debug TSZ_LOG_FORMAT=tree tsz file.ts
15//!
16//! # JSON (for tooling or sharing full traces)
17//! TSZ_LOG=debug TSZ_LOG_FORMAT=json tsz file.ts
18//!
19//! # Plain text (classic fmt subscriber)
20//! TSZ_LOG=debug tsz file.ts
21//!
22//! # Fine-grained filtering
23//! TSZ_LOG="wasm::checker=debug,wasm::solver=trace" TSZ_LOG_FORMAT=tree tsz file.ts
24//! ```
25//!
26//! The subscriber is only initialised when `TSZ_LOG` (or `RUST_LOG`) is set,
27//! so there is zero overhead in normal builds.
28//! `TSZ_PERF` also enables a minimal default perf filter (`wasm::perf=info`).
29
30use tracing_subscriber::prelude::*;
31use tracing_subscriber::{EnvFilter, Registry, fmt};
32
33/// Tracing output format.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum LogFormat {
36    /// Standard flat text lines (default).
37    Text,
38    /// Hierarchical indented tree via `tracing-tree`.
39    Tree,
40    /// Newline-delimited JSON objects.
41    Json,
42}
43
44impl LogFormat {
45    /// Parse from the `TSZ_LOG_FORMAT` environment variable.
46    fn from_env() -> Self {
47        match std::env::var("TSZ_LOG_FORMAT")
48            .unwrap_or_default()
49            .to_lowercase()
50            .as_str()
51        {
52            "tree" => Self::Tree,
53            "json" => Self::Json,
54            _ => Self::Text,
55        }
56    }
57}
58
59/// Build an `EnvFilter` from `TSZ_LOG`, falling back to `RUST_LOG`.
60///
61/// `TSZ_LOG` takes precedence when both are set. Values use the same
62/// syntax as `RUST_LOG` (e.g. `debug`, `wasm::checker=trace`).
63fn build_filter() -> EnvFilter {
64    if let Ok(val) = std::env::var("TSZ_LOG") {
65        EnvFilter::builder().parse_lossy(val)
66    } else {
67        // RUST_LOG is set (caller already checked).  Use it as-is.
68        EnvFilter::from_default_env()
69    }
70}
71
72/// Initialise the global tracing subscriber.
73///
74/// Does nothing when neither `TSZ_LOG` nor `RUST_LOG` is set, keeping startup
75/// cost at zero for normal usage.
76///
77/// All output goes to stderr so it never interferes with stdout
78/// (compiler diagnostics, `--showConfig`, or LSP JSON-RPC).
79pub fn init_tracing() {
80    // Only pay for tracing when explicitly requested.
81    let has_tsz_log = std::env::var("TSZ_LOG").is_ok();
82    let has_rust_log = std::env::var("RUST_LOG").is_ok();
83    let has_perf = std::env::var_os("TSZ_PERF").is_some();
84    if !has_tsz_log && !has_rust_log && !has_perf {
85        return;
86    }
87
88    let filter = if has_tsz_log || has_rust_log {
89        build_filter()
90    } else {
91        EnvFilter::builder().parse_lossy("wasm::perf=info")
92    };
93    let format = LogFormat::from_env();
94
95    match format {
96        LogFormat::Tree => {
97            let tree_layer = tracing_tree::HierarchicalLayer::default()
98                .with_indent_amount(2)
99                .with_indent_lines(true)
100                .with_deferred_spans(true)
101                .with_span_retrace(true)
102                .with_targets(true);
103
104            Registry::default().with(filter).with(tree_layer).init();
105        }
106        LogFormat::Json => {
107            let json_layer = fmt::layer().json().with_writer(std::io::stderr);
108
109            Registry::default().with(filter).with(json_layer).init();
110        }
111        LogFormat::Text => {
112            tracing_subscriber::fmt()
113                .with_env_filter(filter)
114                .with_writer(std::io::stderr)
115                .init();
116        }
117    }
118}