moq_native/
log.rs

1use serde::{Deserialize, Serialize};
2use serde_with::DisplayFromStr;
3use tracing::level_filters::LevelFilter;
4use tracing::Level;
5use tracing_subscriber::layer::SubscriberExt;
6use tracing_subscriber::util::SubscriberInitExt;
7use tracing_subscriber::EnvFilter;
8use tracing_subscriber::Layer;
9
10/// Tracing log configuration.
11#[serde_with::serde_as]
12#[derive(Clone, clap::Parser, Serialize, Deserialize, Debug)]
13#[serde(deny_unknown_fields, default)]
14pub struct Log {
15	/// The level filter to use.
16	#[serde_as(as = "DisplayFromStr")]
17	#[arg(id = "log-level", long = "log-level", default_value = "info", env = "MOQ_LOG_LEVEL")]
18	pub level: Level,
19}
20
21impl Default for Log {
22	fn default() -> Self {
23		Self { level: Level::INFO }
24	}
25}
26
27impl Log {
28	pub fn level(&self) -> LevelFilter {
29		LevelFilter::from_level(self.level)
30	}
31
32	pub fn init(&self) {
33		let filter = EnvFilter::builder()
34			.with_default_directive(self.level().into()) // Default to our -q/-v args
35			.from_env_lossy() // Allow overriding with RUST_LOG
36			.add_directive("h2=warn".parse().unwrap())
37			.add_directive("quinn=info".parse().unwrap())
38			.add_directive("tracing::span=off".parse().unwrap())
39			.add_directive("tracing::span::active=off".parse().unwrap())
40			.add_directive("tokio=info".parse().unwrap())
41			.add_directive("runtime=info".parse().unwrap());
42
43		let fmt_layer = tracing_subscriber::fmt::layer()
44			.with_writer(std::io::stderr)
45			.with_filter(filter);
46
47		#[cfg(feature = "tokio-console")]
48		{
49			let console_layer = console_subscriber::spawn();
50			tracing_subscriber::registry()
51				.with(fmt_layer)
52				.with(console_layer)
53				.init();
54		}
55
56		#[cfg(not(feature = "tokio-console"))]
57		{
58			tracing_subscriber::registry().with(fmt_layer).init();
59		}
60
61		// Start deadlock detection thread (only in debug builds)
62		#[cfg(debug_assertions)]
63		std::thread::spawn(Self::deadlock_detector);
64	}
65
66	#[cfg(debug_assertions)]
67	fn deadlock_detector() {
68		loop {
69			std::thread::sleep(std::time::Duration::from_secs(1));
70
71			let deadlocks = parking_lot::deadlock::check_deadlock();
72			if deadlocks.is_empty() {
73				continue;
74			}
75
76			tracing::error!("DEADLOCK DETECTED");
77
78			for (i, threads) in deadlocks.iter().enumerate() {
79				tracing::error!("Deadlock #{}", i);
80				for t in threads {
81					tracing::error!("Thread Id {:#?}", t.thread_id());
82					tracing::error!("{:#?}", t.backtrace());
83				}
84			}
85
86			// Optionally: std::process::abort() to get a core dump
87		}
88	}
89}