Skip to content

Commit 787f427

Browse files
committed
fix(tracing): avoid SetGlobalDefaultError panic when rolldown devtools is enabled (#1364)
## Summary Fixes a panic that occurs when `build.rolldownOptions.devtools` is set in `vite.config.ts` (commonly via `@vitejs/devtools`): ``` Rolldown panicked. This is a bug in Rolldown, not your code. thread '<unnamed>' panicked at tracing-subscriber-0.3.23/src/util.rs:94:14: failed to set global default subscriber: SetGlobalDefaultError("a global default trace dispatcher has already been set") ``` ## Root cause The vite-plus NAPI binary bundles `rolldown_binding` (including `rolldown_devtools`) into a single `.node` file. Two paths inside this same binary each try to install a global tracing subscriber: 1. **NAPI module load** → `vite_shared::init_tracing()` — previously installed a no-op subscriber (empty `Targets` filter) even when `VITE_LOG` was unset, occupying the global default slot. 2. **Build with devtools enabled** → `DebugTracer::init()` — tries to install its own subscriber and panics because the slot is already taken. ## Fix - Skip subscriber installation entirely when `VITE_LOG` is not set, leaving the global default slot free for `DebugTracer::init()` (the common case — no one pays for tracing they didn't ask for). - Use `.try_init().ok()` instead of `.init()` so a double-init still fails gracefully rather than panicking. ## Test plan - [x] Reproduced the panic at `/tmp/repro-1356` with `@vitejs/devtools@0.1.13` + `build.rolldownOptions.devtools: {}`. - [x] Rebuilt with `RELEASE_BUILD=1 pnpm bootstrap-cli`; `npx vp build` now completes successfully. - [x] `VITE_LOG=debug vp build` still prints traces (subscriber is installed when requested). ## Related Upstream rolldown also makes the same `.init()` → `.try_init()` change as defense-in-depth: rolldown/rolldown#9078. That upstream fix is not required for this patch — the vite-plus fix here is sufficient on its own. Closes #1356
1 parent f98a237 commit 787f427

File tree

1 file changed

+13
-10
lines changed

1 file changed

+13
-10
lines changed

crates/vite_shared/src/tracing.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Tracing initialization for vite-plus
22
3-
use std::sync::OnceLock;
3+
use std::{str::FromStr, sync::OnceLock};
44

55
use tracing_subscriber::{
66
filter::{LevelFilter, Targets},
@@ -14,25 +14,28 @@ use crate::env_vars;
1414
/// Uses `OnceLock` to ensure tracing is only initialized once,
1515
/// even if called multiple times.
1616
///
17+
/// Only sets the global default subscriber when `VITE_LOG` is set.
18+
/// When unset, the global default slot is left free so that other
19+
/// subscribers (e.g., rolldown devtools) can claim it without panicking.
20+
///
1721
/// # Environment Variables
1822
/// - `VITE_LOG`: Controls log filtering (e.g., "debug", "vite_task=trace")
1923
pub fn init_tracing() {
2024
static TRACING: OnceLock<()> = OnceLock::new();
2125
TRACING.get_or_init(|| {
26+
let Ok(env_var) = std::env::var(env_vars::VITE_LOG) else {
27+
return;
28+
};
29+
2230
tracing_subscriber::registry()
2331
.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+
Targets::from_str(&env_var)
33+
.unwrap_or_default()
3234
// disable brush-parser tracing
3335
.with_targets([("tokenize", LevelFilter::OFF), ("parse", LevelFilter::OFF)]),
3436
)
3537
.with(tracing_subscriber::fmt::layer())
36-
.init();
38+
.try_init()
39+
.ok();
3740
});
3841
}

0 commit comments

Comments
 (0)