bitwarden_logging/global.rs
1//! Global Flight Recorder buffer and convenience accessors.
2
3use std::sync::{Arc, OnceLock};
4
5use crate::{CircularBuffer, FlightRecorderConfig, FlightRecorderEvent, FlightRecorderLayer};
6
7/// Global Flight Recorder buffer, initialized during `init_sdk()`.
8static FLIGHT_RECORDER_BUFFER: OnceLock<Arc<CircularBuffer<FlightRecorderEvent>>> = OnceLock::new();
9
10/// Initialize the global Flight Recorder.
11///
12/// Creates a [`FlightRecorderLayer`] and stores the buffer in a global
13/// [`OnceLock`] so it can be read from anywhere via [`read_flight_recorder`].
14/// Returns the layer to add to a tracing subscriber.
15///
16/// If called more than once, the second call's buffer is **not** stored
17/// globally (the `OnceLock` is already set), but the returned layer is
18/// still independently functional.
19#[must_use]
20pub fn init_flight_recorder(config: FlightRecorderConfig) -> FlightRecorderLayer {
21 let layer = FlightRecorderLayer::new(config);
22 let _ = FLIGHT_RECORDER_BUFFER.set(layer.buffer());
23 layer
24}
25
26/// Get the global Flight Recorder buffer.
27///
28/// Returns `None` if [`init_flight_recorder`] has not been called.
29pub fn get_flight_recorder_buffer() -> Option<Arc<CircularBuffer<FlightRecorderEvent>>> {
30 FLIGHT_RECORDER_BUFFER.get().cloned()
31}
32
33/// Read all events from the global Flight Recorder buffer.
34///
35/// Returns an empty `Vec` if [`init_flight_recorder`] has not been called.
36#[must_use]
37pub fn read_flight_recorder() -> Vec<FlightRecorderEvent> {
38 get_flight_recorder_buffer()
39 .map(|buffer| buffer.read())
40 .unwrap_or_default()
41}
42
43/// Get the current event count without reading event contents.
44///
45/// Returns `0` if [`init_flight_recorder`] has not been called.
46#[must_use]
47pub fn flight_recorder_count() -> usize {
48 get_flight_recorder_buffer()
49 .map(|buffer| buffer.len())
50 .unwrap_or(0)
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_config_default_values() {
59 let config = FlightRecorderConfig::default();
60 assert_eq!(config.buffer_size.get(), 1000);
61 assert_eq!(config.level, tracing::Level::DEBUG);
62 }
63
64 #[test]
65 fn test_read_before_init_returns_empty() {
66 // A fresh OnceLock (not the global one, which may already be set
67 // by other tests) would return None. We can at least verify the
68 // convenience functions don't panic.
69 let events = read_flight_recorder();
70 // Either empty (not initialized) or non-empty (another test initialized it)
71 let _ = events;
72 }
73}