Skip to main content

deribit_websocket/utils/
logger.rs

1//! `tracing` subscriber setup, driven by `DERIBIT_LOG_LEVEL`.
2//!
3//! Installs a [`FmtSubscriber`] as the process-global default. The
4//! level comes from the `DERIBIT_LOG_LEVEL` environment variable read
5//! on the first call; subsequent calls are no-ops.
6
7use std::env;
8use std::sync::Once;
9use tracing::Level;
10use tracing_subscriber::FmtSubscriber;
11
12static INIT: Once = Once::new();
13
14/// Install the `tracing` subscriber used by every example and binary
15/// in the crate.
16///
17/// The level is read from the `DERIBIT_LOG_LEVEL` environment variable
18/// once, on the first call. Recognised values: `TRACE`, `DEBUG`,
19/// `INFO`, `WARN`, `ERROR`. Anything else (or an unset variable)
20/// defaults to `INFO`.
21///
22/// The subscriber is registered as the *process-global* default via
23/// [`tracing::subscriber::set_global_default`], which `tracing` allows
24/// only once per process. A [`Once`] guard makes subsequent calls to
25/// this function safe: they return immediately without touching the
26/// already-installed subscriber, so applications that wire logging
27/// from multiple entry points (tests, libs, `main`) do not race.
28///
29/// Changing `DERIBIT_LOG_LEVEL` after the first call has no effect —
30/// tracing does not support replacing the global default at runtime.
31///
32/// # Example
33///
34/// ```rust,no_run
35/// use deribit_websocket::utils::setup_logger;
36///
37/// // Set log level via environment variable (unsafe in Rust 2024 edition)
38/// unsafe {
39///     std::env::set_var("DERIBIT_LOG_LEVEL", "DEBUG");
40/// }
41///
42/// // Initialize the logger
43/// setup_logger();
44///
45/// // Now you can use tracing macros
46/// tracing::info!("Logger initialized");
47/// ```
48pub fn setup_logger() {
49    INIT.call_once(|| {
50        let log_level = env::var("DERIBIT_LOG_LEVEL")
51            .unwrap_or_else(|_| "INFO".to_string())
52            .to_uppercase();
53
54        let level = match log_level.as_str() {
55            "DEBUG" => Level::DEBUG,
56            "ERROR" => Level::ERROR,
57            "WARN" => Level::WARN,
58            "TRACE" => Level::TRACE,
59            _ => Level::INFO,
60        };
61
62        let subscriber = FmtSubscriber::builder().with_max_level(level).finish();
63
64        if tracing::subscriber::set_global_default(subscriber).is_ok() {
65            tracing::debug!("Log level set to: {}", level);
66        }
67    });
68}
69
70#[cfg(test)]
71#[allow(clippy::unwrap_used, clippy::expect_used)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_setup_logger_can_be_called_multiple_times() {
77        // First call
78        setup_logger();
79        // Second call should not panic
80        setup_logger();
81    }
82}