Skip to main content

moq_native/
log.rs

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