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) -> crate::Result<()> {
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()?)
42			.add_directive("quinn=info".parse()?)
43			.add_directive("tungstenite=info".parse()?)
44			.add_directive("rustls=info".parse()?)
45			.add_directive("tracing::span=off".parse()?)
46			.add_directive("tracing::span::active=off".parse()?)
47			.add_directive("tokio=info".parse()?)
48			.add_directive("runtime=info".parse()?);
49
50		let registry = tracing_subscriber::registry();
51
52		// On Android, route logs to logcat so they can be inspected via ADB/Android Studio.
53		// Everywhere else, format to stderr.
54		#[cfg(all(target_os = "android", feature = "android-logcat"))]
55		let registry = {
56			let logcat_layer = tracing_android::layer("MoQNative")
57				.map_err(|e| crate::Error::Logcat(std::sync::Arc::new(e)))?
58				.with_filter(filter);
59			registry.with(logcat_layer)
60		};
61
62		#[cfg(not(all(target_os = "android", feature = "android-logcat")))]
63		let registry = {
64			let fmt_layer = tracing_subscriber::fmt::layer()
65				.with_writer(std::io::stderr)
66				.with_filter(filter);
67			registry.with(fmt_layer)
68		};
69
70		registry
71			.try_init()
72			.map_err(|e| crate::Error::SetSubscriber(std::sync::Arc::new(e)))?;
73
74		// Start deadlock detection thread (only in debug builds)
75		#[cfg(debug_assertions)]
76		std::thread::spawn(Self::deadlock_detector);
77
78		Ok(())
79	}
80
81	#[cfg(debug_assertions)]
82	fn deadlock_detector() {
83		loop {
84			std::thread::sleep(std::time::Duration::from_secs(1));
85
86			let deadlocks = parking_lot::deadlock::check_deadlock();
87			if deadlocks.is_empty() {
88				continue;
89			}
90
91			tracing::error!("DEADLOCK DETECTED");
92
93			for (i, threads) in deadlocks.iter().enumerate() {
94				tracing::error!("Deadlock #{}", i);
95				for t in threads {
96					tracing::error!("Thread Id {:#?}", t.thread_id());
97					tracing::error!("{:#?}", t.backtrace());
98				}
99			}
100
101			// Optionally: std::process::abort() to get a core dump
102		}
103	}
104}