Skip to main content

deribit_http/
logger.rs

1//! Logger setup for the Deribit HTTP client
2//!
3//! This module provides cross-platform logging setup using tracing.
4//! - Native: Uses `tracing_subscriber::FmtSubscriber` with env var configuration
5//! - WASM: Uses `tracing-web` to route logs to browser/Worker console
6
7use std::sync::Once;
8
9#[cfg(not(target_arch = "wasm32"))]
10use std::env;
11#[cfg(not(target_arch = "wasm32"))]
12use tracing::Level;
13#[cfg(not(target_arch = "wasm32"))]
14use tracing_subscriber::FmtSubscriber;
15
16#[cfg(target_arch = "wasm32")]
17use tracing_subscriber::Layer;
18#[cfg(target_arch = "wasm32")]
19use tracing_subscriber::layer::SubscriberExt;
20#[cfg(target_arch = "wasm32")]
21use tracing_subscriber::util::SubscriberInitExt;
22#[cfg(target_arch = "wasm32")]
23use tracing_web::MakeWebConsoleWriter;
24
25static INIT: Once = Once::new();
26
27/// Sets up the logger for the application.
28///
29/// - **Native**: Log level is determined by `DERIBIT_LOG_LEVEL` env var (defaults to INFO)
30/// - **WASM**: Logs to browser/Worker console at INFO level
31#[cfg(not(target_arch = "wasm32"))]
32pub fn setup_logger() {
33    INIT.call_once(|| {
34        let log_level = env::var("DERIBIT_LOG_LEVEL")
35            .unwrap_or_else(|_| "INFO".to_string())
36            .to_uppercase();
37
38        let level = match log_level.as_str() {
39            "DEBUG" => Level::DEBUG,
40            "ERROR" => Level::ERROR,
41            "WARN" => Level::WARN,
42            "TRACE" => Level::TRACE,
43            _ => Level::INFO,
44        };
45
46        let subscriber = FmtSubscriber::builder().with_max_level(level).finish();
47
48        tracing::subscriber::set_global_default(subscriber)
49            .expect("Error setting default subscriber");
50
51        tracing::debug!("Log level set to: {}", level);
52    });
53}
54
55/// Sets up the logger for the application.
56///
57/// - **Native**: Log level is determined by `DERIBIT_LOG_LEVEL` env var (defaults to INFO)
58/// - **WASM**: Logs to browser/Worker console at INFO level
59#[cfg(target_arch = "wasm32")]
60pub fn setup_logger() {
61    INIT.call_once(|| {
62        let fmt_layer = tracing_subscriber::fmt::layer()
63            .with_ansi(false)
64            .without_time()
65            .with_writer(MakeWebConsoleWriter::new())
66            .with_filter(tracing_subscriber::filter::LevelFilter::INFO);
67
68        tracing_subscriber::registry().with(fmt_layer).init();
69
70        tracing::debug!("WASM logger initialized");
71    });
72}
73
74#[cfg(all(test, not(target_arch = "wasm32")))]
75mod tests_setup_logger {
76    use super::setup_logger;
77    use std::env;
78    use tracing::subscriber::set_global_default;
79    use tracing_subscriber::FmtSubscriber;
80
81    #[test]
82    fn test_logger_initialization_info() {
83        unsafe {
84            env::set_var("DERIBIT_LOG_LEVEL", "INFO");
85        }
86        setup_logger();
87
88        // After setting up the logger, you would typically assert that the logger is working
89        // However, due to the nature of logging, it's difficult to directly assert on log output.
90        // You can, however, check that set_global_default has been called successfully without panic.
91        assert!(
92            set_global_default(FmtSubscriber::builder().finish()).is_err(),
93            "Logger should already be set"
94        );
95    }
96
97    #[test]
98    fn test_logger_initialization_debug() {
99        unsafe {
100            env::set_var("DERIBIT_LOG_LEVEL", "DEBUG");
101        }
102        setup_logger();
103
104        // Similar to the previous test, check that the global logger has been set
105        assert!(
106            set_global_default(FmtSubscriber::builder().finish()).is_err(),
107            "Logger should already be set"
108        );
109    }
110
111    #[test]
112    fn test_logger_initialization_default() {
113        unsafe {
114            env::remove_var("DERIBIT_LOG_LEVEL");
115        }
116        setup_logger();
117
118        // Check that the global logger has been set
119        assert!(
120            set_global_default(FmtSubscriber::builder().finish()).is_err(),
121            "Logger should already be set"
122        );
123    }
124
125    #[test]
126    fn test_logger_called_once() {
127        unsafe {
128            env::set_var("DERIBIT_LOG_LEVEL", "INFO");
129        }
130
131        setup_logger(); // First call should set up the logger
132        setup_logger(); // Second call should not re-initialize
133
134        // Check that the global logger has been set only once
135        assert!(
136            set_global_default(FmtSubscriber::builder().finish()).is_err(),
137            "Logger should already be set and should not be reset"
138        );
139    }
140}
141
142#[cfg(all(test, not(target_arch = "wasm32")))]
143mod tests_setup_logger_bis {
144    use super::*;
145    use std::sync::Mutex;
146    use tracing::info;
147    use tracing::subscriber::with_default;
148    use tracing_subscriber::Layer;
149    use tracing_subscriber::layer::SubscriberExt;
150
151    static TEST_MUTEX: Mutex<()> = Mutex::new(());
152
153    #[derive(Clone)]
154    struct TestLayer {
155        level: std::sync::Arc<Mutex<Option<Level>>>,
156    }
157
158    impl<S> Layer<S> for TestLayer
159    where
160        S: tracing::Subscriber,
161    {
162        fn on_event(
163            &self,
164            event: &tracing::Event<'_>,
165            _ctx: tracing_subscriber::layer::Context<'_, S>,
166        ) {
167            let mut level = self.level.lock().unwrap();
168            *level = Some(*event.metadata().level());
169        }
170    }
171
172    fn create_test_layer() -> (TestLayer, std::sync::Arc<Mutex<Option<Level>>>) {
173        let level = std::sync::Arc::new(Mutex::new(None));
174        (
175            TestLayer {
176                level: level.clone(),
177            },
178            level,
179        )
180    }
181
182    #[test]
183    fn test_default_log_level() {
184        let _lock = TEST_MUTEX.lock().unwrap();
185        unsafe {
186            env::remove_var("DERIBIT_LOG_LEVEL");
187        }
188
189        let (layer, level) = create_test_layer();
190        let subscriber = tracing_subscriber::registry().with(layer);
191
192        with_default(subscriber, || {
193            setup_logger();
194            info!("Test log");
195        });
196
197        assert_eq!(*level.lock().unwrap(), Some(Level::INFO));
198    }
199
200    #[test]
201    fn test_debug_log_level() {
202        let _lock = TEST_MUTEX.lock().unwrap();
203        unsafe {
204            env::set_var("DERIBIT_LOG_LEVEL", "DEBUG");
205        }
206
207        let (layer, level) = create_test_layer();
208        let subscriber = tracing_subscriber::registry().with(layer);
209
210        with_default(subscriber, || {
211            setup_logger();
212            tracing::debug!("Test log");
213        });
214
215        assert_eq!(*level.lock().unwrap(), Some(Level::DEBUG));
216
217        unsafe {
218            env::remove_var("DERIBIT_LOG_LEVEL");
219        }
220    }
221
222    #[test]
223    fn test_error_log_level() {
224        let _lock = TEST_MUTEX.lock().unwrap();
225        unsafe {
226            env::set_var("DERIBIT_LOG_LEVEL", "ERROR");
227        }
228
229        let (layer, level) = create_test_layer();
230        let subscriber = tracing_subscriber::registry().with(layer);
231
232        with_default(subscriber, || {
233            setup_logger();
234            tracing::error!("Test log");
235        });
236
237        assert_eq!(*level.lock().unwrap(), Some(Level::ERROR));
238        unsafe {
239            env::remove_var("DERIBIT_LOG_LEVEL");
240        }
241    }
242
243    #[test]
244    fn test_warn_log_level() {
245        let _lock = TEST_MUTEX.lock().unwrap();
246        unsafe {
247            env::set_var("DERIBIT_LOG_LEVEL", "WARN");
248        }
249
250        let (layer, level) = create_test_layer();
251        let subscriber = tracing_subscriber::registry().with(layer);
252
253        with_default(subscriber, || {
254            setup_logger();
255            tracing::warn!("Test log");
256        });
257
258        assert_eq!(*level.lock().unwrap(), Some(Level::WARN));
259        unsafe {
260            env::remove_var("DERIBIT_LOG_LEVEL");
261        }
262    }
263
264    #[test]
265    fn test_trace_log_level() {
266        let _lock = TEST_MUTEX.lock().unwrap();
267        unsafe {
268            env::set_var("DERIBIT_LOG_LEVEL", "TRACE");
269        }
270
271        let (layer, level) = create_test_layer();
272        let subscriber = tracing_subscriber::registry().with(layer);
273
274        with_default(subscriber, || {
275            setup_logger();
276            tracing::trace!("Test log");
277        });
278
279        assert_eq!(*level.lock().unwrap(), Some(Level::TRACE));
280        unsafe {
281            env::remove_var("DERIBIT_LOG_LEVEL");
282        }
283    }
284
285    #[test]
286    fn test_invalid_log_level() {
287        let _lock = TEST_MUTEX.lock().unwrap();
288        unsafe {
289            env::set_var("DERIBIT_LOG_LEVEL", "INVALID");
290        }
291
292        let (layer, level) = create_test_layer();
293        let subscriber = tracing_subscriber::registry().with(layer);
294
295        with_default(subscriber, || {
296            setup_logger();
297            info!("Test log");
298        });
299
300        assert_eq!(*level.lock().unwrap(), Some(Level::INFO));
301        unsafe {
302            env::remove_var("DERIBIT_LOG_LEVEL");
303        }
304    }
305}