Skip to main content

sqlite_graphrag/
telemetry.rs

1//! Centralized tracing subscriber initialization.
2//!
3//! Configures the global subscriber with JSON or pretty format,
4//! installs the panic hook and the log-to-tracing bridge.
5
6use tracing_subscriber::EnvFilter;
7
8/// Initializes the global tracing subscriber, panic hook, and log bridge.
9///
10/// Must be called exactly once, before any tracing events are emitted.
11/// After this call, panics on any thread produce `tracing::error!` events,
12/// and `log` crate events from dependencies (refinery, ureq, ort) are
13/// forwarded to the tracing subscriber.
14pub fn init_tracing(log_level: &str, log_format: &str) {
15    // TR02: the log→tracing bridge is activated automatically by
16    // tracing-subscriber's built-in `tracing-log` feature (default).
17    // Calling LogTracer::init() separately would conflict with the
18    // global logger that tracing-subscriber installs via .init().
19    let use_ansi = crate::terminal::should_use_ansi();
20
21    if log_format == "json" {
22        tracing_subscriber::fmt()
23            .json()
24            .with_ansi(false)
25            .with_thread_ids(true)
26            .with_thread_names(true)
27            .with_env_filter(EnvFilter::new(log_level))
28            .with_writer(std::io::stderr)
29            .init();
30    } else {
31        tracing_subscriber::fmt()
32            .with_ansi(use_ansi)
33            .with_env_filter(EnvFilter::new(log_level))
34            .with_writer(std::io::stderr)
35            .init();
36    }
37
38    // TR05: confirm effective filter after init
39    tracing::debug!(
40        target: "telemetry",
41        filter = %log_level,
42        format = %log_format,
43        ansi = use_ansi,
44        "tracing subscriber initialized"
45    );
46
47    // TR01 (v1.0.80, A1/G2): panic hook emits a structured tracing::error!
48    // and DELIBERATELY DOES NOT call the previous hook. The default Rust
49    // panic hook prints the same payload + location to stderr; combined
50    // with the tracing event below, that produces a double-trace (one
51    // structured event in JSON or pretty, one unstructured dump). We
52    // prefer the structured single-trace: the tracing event carries the
53    // same payload and location fields and is captured by the global
54    // subscriber. Test runs still fail on panic because Rust aborts the
55    // process regardless of which hook is installed.
56    std::panic::set_hook(Box::new(|info| {
57        let payload = info
58            .payload()
59            .downcast_ref::<&str>()
60            .copied()
61            .or_else(|| info.payload().downcast_ref::<String>().map(|s| s.as_str()))
62            .unwrap_or("<non-string panic>");
63        let location = info
64            .location()
65            .map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column()));
66        tracing::error!(
67            target: "panic",
68            message = %payload,
69            location = location.as_deref().unwrap_or("unknown"),
70            "thread panicked"
71        );
72    }));
73}