reifydb_core/interface/logging/
mod.rs1use std::{
5 collections::HashMap,
6 fmt,
7 fmt::Debug,
8 sync::{Mutex, OnceLock},
9 thread::current,
10};
11
12use crossbeam_channel::{SendError, Sender};
13use reifydb_type::{IntoValue, Value, value::DateTime};
14use serde::{Deserialize, Serialize};
15
16use crate::util;
17
18mod macros;
19pub mod mock;
20pub mod timed;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
23pub enum LogLevel {
24 Off = 0,
25 Trace = 1,
26 Debug = 2,
27 Info = 3,
28 Warn = 4,
29 Error = 5,
30 Critical = 6,
31}
32
33impl LogLevel {
34 pub fn as_str(&self) -> &'static str {
35 match self {
36 LogLevel::Off => "off",
37 LogLevel::Trace => "trace",
38 LogLevel::Debug => "debug",
39 LogLevel::Info => "info",
40 LogLevel::Warn => "warn",
41 LogLevel::Error => "error",
42 LogLevel::Critical => "critical",
43 }
44 }
45}
46
47impl fmt::Display for LogLevel {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "{}", self.as_str())
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct Record {
56 pub timestamp: DateTime,
58 pub level: LogLevel,
60 pub module: String,
62 pub message: String,
64 pub fields: HashMap<Value, Value>,
66 pub file: Option<String>,
68 pub line: Option<u32>,
70 pub thread_id: String,
72}
73
74impl Record {
75 pub fn new(level: LogLevel, module: impl Into<String>, message: impl Into<String>) -> Self {
76 Self {
77 timestamp: util::now(),
78 level,
79 module: module.into(),
80 message: message.into(),
81 fields: HashMap::new(),
82 file: None,
83 line: None,
84 thread_id: format!("{:?}", current().id()),
85 }
86 }
87
88 pub fn with_field(mut self, key: impl IntoValue, value: impl IntoValue) -> Self {
89 self.fields.insert(key.into_value(), value.into_value());
90 self
91 }
92
93 pub fn with_location(mut self, file: impl Into<String>, line: u32) -> Self {
94 self.file = Some(file.into());
95 self.line = Some(line);
96 self
97 }
98}
99
100pub trait LogBackend: Send + Sync + Debug {
101 fn name(&self) -> &str;
102
103 fn write(&self, records: &[Record]) -> crate::Result<()>;
104
105 fn flush(&self) -> crate::Result<()> {
106 Ok(())
107 }
108}
109
110pub struct Logger {
111 sender: Sender<Record>,
112}
113
114impl Logger {
115 pub fn new(sender: Sender<Record>) -> Self {
117 Self {
118 sender,
119 }
120 }
121
122 pub fn log(&self, record: Record) -> Result<(), SendError<Record>> {
124 self.sender.send(record)
125 }
126}
127
128static LOGGER: OnceLock<Logger> = OnceLock::new();
130
131const MAX_BUFFERED_RECORDS: usize = 10_000;
133
134static LOG_BUFFER: OnceLock<Mutex<Vec<Record>>> = OnceLock::new();
136
137fn get_log_buffer() -> &'static Mutex<Vec<Record>> {
139 LOG_BUFFER.get_or_init(|| Mutex::new(Vec::with_capacity(1000)))
140}
141
142pub fn init_logger(sender: Sender<Record>) {
145 let logger = Logger::new(sender);
146
147 if LOGGER.set(logger).is_ok() {
149 if let Ok(mut buffer) = get_log_buffer().lock() {
150 if !buffer.is_empty() {
151 if let Some(logger) = LOGGER.get() {
152 for record in buffer.drain(..) {
153 let _ = logger.log(record);
154 }
155 }
156 }
157 }
158 }
159}
160
161pub fn logger() -> Option<&'static Logger> {
163 LOGGER.get()
164}
165
166pub fn log(record: Record) {
169 #[cfg(debug_assertions)]
171 {
172 if let Some(sender) = mock::get_mock_logger() {
173 let _ = sender.send(record);
175 return;
176 }
177 }
178
179 if let Some(logger) = logger() {
181 let _ = logger.log(record);
183 } else {
184 if let Ok(mut buffer) = get_log_buffer().lock() {
186 if buffer.len() < MAX_BUFFERED_RECORDS {
189 buffer.push(record);
190 }
191 }
195 }
196}