Skip to main content

provide_telemetry/
logger.rs

1// SPDX-FileCopyrightText: Copyright (C) 2026 provide.io llc
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-Comment: Part of provide-telemetry.
4//
5
6use std::collections::BTreeMap;
7use std::sync::{Arc, LazyLock, Mutex, OnceLock};
8
9use serde_json::Value;
10
11use crate::context::get_context;
12use crate::tracer::get_trace_context;
13
14const MAX_FALLBACK_EVENTS: usize = 1000;
15
16#[derive(Clone, Debug, PartialEq)]
17pub struct LogEvent {
18    pub level: String,
19    pub target: String,
20    pub message: String,
21    pub context: BTreeMap<String, Value>,
22    pub trace_id: Option<String>,
23    pub span_id: Option<String>,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct Logger {
28    target: String,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct NullLogger {
33    target: String,
34}
35
36#[derive(Clone, Debug)]
37pub struct BufferLogger {
38    target: String,
39    events: Arc<Mutex<Vec<LogEvent>>>,
40}
41
42static EVENTS: OnceLock<Mutex<Vec<LogEvent>>> = OnceLock::new();
43
44fn events() -> &'static Mutex<Vec<LogEvent>> {
45    EVENTS.get_or_init(|| Mutex::new(Vec::new()))
46}
47
48pub static logger: LazyLock<Logger> = LazyLock::new(|| Logger::new(None));
49
50fn new_event(target: &str, level: &str, message: &str) -> LogEvent {
51    let trace = get_trace_context();
52    LogEvent {
53        level: level.to_string(),
54        target: target.to_string(),
55        message: message.to_string(),
56        context: get_context(),
57        trace_id: trace.get("trace_id").and_then(Clone::clone),
58        span_id: trace.get("span_id").and_then(Clone::clone),
59    }
60}
61
62impl Logger {
63    pub fn new(target: Option<&str>) -> Self {
64        Self {
65            target: target.unwrap_or("provide.telemetry").to_string(),
66        }
67    }
68
69    pub fn target(&self) -> &str {
70        &self.target
71    }
72
73    pub fn debug(&self, message: &str) {
74        self.log("DEBUG", message);
75    }
76
77    pub fn info(&self, message: &str) {
78        self.log("INFO", message);
79    }
80
81    pub fn warn(&self, message: &str) {
82        self.log("WARN", message);
83    }
84
85    pub fn error(&self, message: &str) {
86        self.log("ERROR", message);
87    }
88
89    pub fn log(&self, level: &str, message: &str) {
90        let event = new_event(&self.target, level, message);
91        let mut buf = events().lock().expect("logger event lock poisoned");
92        if buf.len() < MAX_FALLBACK_EVENTS {
93            buf.push(event);
94        }
95    }
96
97    pub fn drain_events_for_tests() -> Vec<LogEvent> {
98        std::mem::take(&mut *events().lock().expect("logger event lock poisoned"))
99    }
100}
101
102impl NullLogger {
103    pub fn new(target: Option<&str>) -> Self {
104        Self {
105            target: target.unwrap_or("provide.telemetry").to_string(),
106        }
107    }
108
109    pub fn target(&self) -> &str {
110        &self.target
111    }
112
113    pub fn debug(&self, _message: &str) {}
114
115    pub fn info(&self, _message: &str) {}
116
117    pub fn warn(&self, _message: &str) {}
118
119    pub fn error(&self, _message: &str) {}
120}
121
122impl BufferLogger {
123    pub fn new(target: Option<&str>) -> Self {
124        Self {
125            target: target.unwrap_or("provide.telemetry").to_string(),
126            events: Arc::new(Mutex::new(Vec::new())),
127        }
128    }
129
130    pub fn target(&self) -> &str {
131        &self.target
132    }
133
134    pub fn debug(&self, message: &str) {
135        self.log("DEBUG", message);
136    }
137
138    pub fn info(&self, message: &str) {
139        self.log("INFO", message);
140    }
141
142    pub fn warn(&self, message: &str) {
143        self.log("WARN", message);
144    }
145
146    pub fn error(&self, message: &str) {
147        self.log("ERROR", message);
148    }
149
150    pub fn log(&self, level: &str, message: &str) {
151        self.events
152            .lock()
153            .expect("buffer logger event lock poisoned")
154            .push(new_event(&self.target, level, message));
155    }
156
157    pub fn drain(&self) -> Vec<LogEvent> {
158        std::mem::take(
159            &mut *self
160                .events
161                .lock()
162                .expect("buffer logger event lock poisoned"),
163        )
164    }
165}
166
167pub fn get_logger(name: Option<&str>) -> Logger {
168    Logger::new(name)
169}
170
171pub fn null_logger(name: Option<&str>) -> NullLogger {
172    NullLogger::new(name)
173}
174
175pub fn buffer_logger(name: Option<&str>) -> BufferLogger {
176    BufferLogger::new(name)
177}