|
1 | 1 | //! Tracing initialization for vite-plus |
| 2 | +//! |
| 3 | +//! ## Environment Variables |
| 4 | +//! - `VITE_LOG`: Controls log filtering (e.g., `"debug"`, `"vite_task=trace"`) |
| 5 | +//! - `VITE_LOG_OUTPUT`: Output format — `"chrome-json"` for Chrome DevTools timeline, |
| 6 | +//! `"readable"` for pretty-printed output, or default stdout. |
2 | 7 |
|
3 | | -use std::sync::OnceLock; |
| 8 | +use std::any::Any; |
| 9 | +use std::sync::atomic::AtomicBool; |
4 | 10 |
|
| 11 | +use tracing_chrome::ChromeLayerBuilder; |
5 | 12 | use tracing_subscriber::{ |
6 | 13 | filter::{LevelFilter, Targets}, |
| 14 | + fmt::{self, format::FmtSpan}, |
7 | 15 | prelude::*, |
8 | 16 | }; |
9 | 17 |
|
10 | 18 | use crate::env_vars; |
11 | 19 |
|
12 | | -/// Initialize tracing with VITE_LOG environment variable. |
| 20 | +static IS_INITIALIZED: AtomicBool = AtomicBool::new(false); |
| 21 | + |
| 22 | +/// Initialize tracing with `VITE_LOG` and `VITE_LOG_OUTPUT` environment variables. |
13 | 23 | /// |
14 | | -/// Uses `OnceLock` to ensure tracing is only initialized once, |
15 | | -/// even if called multiple times. |
| 24 | +/// Returns an optional guard that must be kept alive for the duration of the |
| 25 | +/// program when using file-based output (e.g., `chrome-json`). Dropping the |
| 26 | +/// guard flushes and finalizes the trace file. |
16 | 27 | /// |
17 | | -/// # Environment Variables |
18 | | -/// - `VITE_LOG`: Controls log filtering (e.g., "debug", "vite_task=trace") |
19 | | -pub fn init_tracing() { |
20 | | - static TRACING: OnceLock<()> = OnceLock::new(); |
21 | | - TRACING.get_or_init(|| { |
22 | | - tracing_subscriber::registry() |
23 | | - .with( |
24 | | - std::env::var(env_vars::VITE_LOG) |
25 | | - .map_or_else( |
26 | | - |_| Targets::new(), |
27 | | - |env_var| { |
28 | | - use std::str::FromStr; |
29 | | - Targets::from_str(&env_var).unwrap_or_default() |
30 | | - }, |
31 | | - ) |
32 | | - // disable brush-parser tracing |
33 | | - .with_targets([("tokenize", LevelFilter::OFF), ("parse", LevelFilter::OFF)]), |
34 | | - ) |
35 | | - .with(tracing_subscriber::fmt::layer()) |
36 | | - .init(); |
37 | | - }); |
| 28 | +/// Uses `AtomicBool` to ensure tracing is only initialized once. |
| 29 | +pub fn init_tracing() -> Option<Box<dyn Any + Send>> { |
| 30 | + if IS_INITIALIZED.swap(true, std::sync::atomic::Ordering::SeqCst) { |
| 31 | + return None; |
| 32 | + } |
| 33 | + |
| 34 | + let Ok(env_var) = std::env::var(env_vars::VITE_LOG) else { |
| 35 | + // Tracing is disabled by default (performance sensitive) |
| 36 | + return None; |
| 37 | + }; |
| 38 | + |
| 39 | + let targets = { |
| 40 | + use std::str::FromStr; |
| 41 | + Targets::from_str(&env_var) |
| 42 | + .unwrap_or_default() |
| 43 | + // disable brush-parser tracing |
| 44 | + .with_targets([("tokenize", LevelFilter::OFF), ("parse", LevelFilter::OFF)]) |
| 45 | + }; |
| 46 | + |
| 47 | + let output_mode = std::env::var(env_vars::VITE_LOG_OUTPUT) |
| 48 | + .unwrap_or_else(|_| "stdout".to_string()); |
| 49 | + |
| 50 | + match output_mode.as_str() { |
| 51 | + "chrome-json" => { |
| 52 | + let (chrome_layer, guard) = ChromeLayerBuilder::new() |
| 53 | + .trace_style(tracing_chrome::TraceStyle::Async) |
| 54 | + .include_args(true) |
| 55 | + .build(); |
| 56 | + tracing_subscriber::registry() |
| 57 | + .with(targets) |
| 58 | + .with(chrome_layer) |
| 59 | + .init(); |
| 60 | + Some(Box::new(guard)) |
| 61 | + } |
| 62 | + "readable" => { |
| 63 | + tracing_subscriber::registry() |
| 64 | + .with(targets) |
| 65 | + .with( |
| 66 | + fmt::layer() |
| 67 | + .pretty() |
| 68 | + .with_span_events(FmtSpan::NONE) |
| 69 | + .with_level(true) |
| 70 | + .with_target(false), |
| 71 | + ) |
| 72 | + .init(); |
| 73 | + None |
| 74 | + } |
| 75 | + _ => { |
| 76 | + // Default: stdout with span events |
| 77 | + tracing_subscriber::registry() |
| 78 | + .with(targets) |
| 79 | + .with(fmt::layer().with_span_events(FmtSpan::CLOSE | FmtSpan::ENTER)) |
| 80 | + .init(); |
| 81 | + None |
| 82 | + } |
| 83 | + } |
38 | 84 | } |
0 commit comments