tracing panic fix
Guide for tracing panic fix
Tracing-Subscriber Double-Initialization Panic Fix
Problem
When running the Legends of Hastinapur game client, it crashes immediately at startup with:
thread 'main' (19072) panicked at /home/ecom-nithinm/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-subscriber-0.3.22/src/util.rs:94:14:
failed to set global default subscriber: SetGlobalDefaultError("a global default trace dispatcher has already been set")Root Cause
Conflict between two tracing initializations:
- Bevy's
LogPlugin(part ofDefaultPlugins):- Automatically initializes
tracing-subscriberfor logging - Calls
.init()on the global subscriber
- Automatically initializes
- Custom
TelemetryPlugin(insrc/systems/telemetry.rs):- Also initializes
tracing-subscriberwith custom configuration - Calls
.init()on the global subscriber (line 49)
- Also initializes
Result: The second
.init() call fails because Rust's tracing-subscriber only allows one global subscriber per process.Solution
Disable Bevy's
LogPlugin when using custom telemetry initialization.Code Change
File:
src/lib.rsBefore:
app.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Legends of Hastinapur".into(),
resolution: (1280., 720.).into(),
..default()
}),
..default()
}))After:
app.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Legends of Hastinapur".into(),
resolution: (1280., 720.).into(),
..default()
}),
..default()
}).disable::<bevy::log::LogPlugin>())Why This Works
DefaultPluginsis a plugin group that includesLogPlugin.disable::<bevy::log::LogPlugin>()removesLogPluginfrom the group- This prevents Bevy from initializing its own tracing subscriber
- Our custom
TelemetryPlugincan now safely initialize the global subscriber
Custom Telemetry Setup
File:
src/systems/telemetry.rsOur custom telemetry provides:
- Structured logging with
tracing-subscriber - Prometheus metrics on
http://localhost:9000/metrics - Sentry error tracking (configured via
SENTRY_DSNenv var)
pub fn init_telemetry() {
// Initialize Sentry
let sentry_dsn = std::env::var("SENTRY_DSN").unwrap_or_default();
let _guard = if !sentry_dsn.is_empty() {
Some(sentry::init((sentry_dsn, sentry::ClientOptions {
release: sentry::release_name!(),
..Default::default()
})))
} else {
None
};
// Initialize Tracing with custom layers
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "info,legends_client=debug".into());
let fmt_layer = tracing_subscriber::fmt::layer()
.with_target(false)
.with_thread_ids(true)
.with_level(true);
let sentry_layer = sentry_tracing::layer();
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.with(sentry_layer)
.init(); // ← This is the ONLY .init() call now
// Initialize Prometheus metrics
let builder = PrometheusBuilder::new();
builder
.with_http_listener(([0, 0, 0, 0], 9000))
.install()
.expect("failed to install Prometheus recorder");
}Verification
After applying the fix:
cargo run --bin legends_clientExpected output (no panic):
warning: `legends_client` (lib) generated 17 warnings
Finished `dev` profile [optimized + debuginfo] target(s) in 24.74s
Running `target/debug/legends_client`
2026-01-05T15:04:15.024388Z INFO ThreadId(01) Telemetry initialized. Metrics at http://localhost:9000/metrics
2026-01-05T15:04:15.025050Z INFO ThreadId(01) No plugin public key found at secrets/plugin_pub.key. Signature verification will be skipped (DEV) or fail (PROD).
2026-01-05T15:04:15.049447Z INFO ThreadId(01) Creating new window "Legends of Hastinapur" (Entity { index: 0, generation: 1 })Verification Status: ✅ CONFIRMED - The game launches successfully without the tracing panic.
Original Expected Output
2026-01-05T14:59:22.476265Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Linux 24.04 Ubuntu", kernel: "6.8.0-49-generic", cpu: "Intel(R) Core(TM) Ultra 9 185H", core_count: "16", memory: "30.8 GiB" }
2026-01-05T14:59:22.634362Z INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA RTX 3000 Ada Generation Laptop GPU", ... }
Telemetry initialized. Metrics at http://localhost:9000/metricsGeneral Pattern for Bevy + Custom Telemetry
When integrating custom observability tools with Bevy:
- Always disable
LogPluginif you're initializingtracing-subscriberyourself - Initialize telemetry early in the plugin chain (before game systems)
- Use
TelemetryPlugin::build()to ensure initialization happens during app setup
app
.add_plugins(DefaultPlugins.disable::<bevy::log::LogPlugin>())
.add_plugins(TelemetryPlugin) // Custom telemetry first
.add_plugins(GamePlugins) // Then game systemsRelated Issues
- Headless mode: If running in headless mode (no rendering), use
MinimalPluginsinstead ofDefaultPluginsto avoid this issue entirely - Multiple binaries: If you have multiple binaries (client, server, editor), ensure each disables
LogPluginif using custom telemetry
Dependencies
[dependencies]
sentry = { version = "0.36", features = ["tracing"] }
sentry-tracing = "0.36"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
metrics = "0.24"
metrics-exporter-prometheus = "0.16"