Skip to main content

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}