reifydb_core/interface/logging/
timed.rs1pub fn format_duration_nanos(nanos: u128) -> String {
8 if nanos < 1_000 {
9 format!("{}ns", nanos)
10 } else if nanos < 1_000_000 {
11 format!("{:.2}μs", nanos as f64 / 1_000.0)
12 } else if nanos < 1_000_000_000 {
13 format!("{:.2}ms", nanos as f64 / 1_000_000.0)
14 } else {
15 format!("{:.2}s", nanos as f64 / 1_000_000_000.0)
16 }
17}
18
19#[macro_export]
21macro_rules! log_timed {
22 ($level:expr, $msg:expr, $code:block) => {{
24 let __start = std::time::Instant::now();
25 let __result = $code;
26 let __elapsed = __start.elapsed().as_nanos();
27 let __duration_str = $crate::interface::logging::timed::format_duration_nanos(__elapsed);
28 let __message = format!("{} (took {})", format!($msg), __duration_str);
29
30 let __record = $crate::interface::logging::Record::new(
31 $level,
32 module_path!(),
33 __message,
34 )
35 .with_location(file!(), line!());
36 $crate::interface::logging::log(__record);
37 __result
38 }};
39
40 ($level:expr, $fmt:expr, $($arg:tt)*; $code:block) => {{
42 let __start = std::time::Instant::now();
43 let __result = $code;
44 let __elapsed = __start.elapsed().as_nanos();
45 let __duration_str = $crate::interface::logging::timed::format_duration_nanos(__elapsed);
46 let __message = format!("{} (took {})", format!($fmt, $($arg)*), __duration_str);
47
48 let __record = $crate::interface::logging::Record::new(
49 $level,
50 module_path!(),
51 __message,
52 )
53 .with_location(file!(), line!());
54 $crate::interface::logging::log(__record);
55 __result
56 }};
57}
58
59#[macro_export]
61macro_rules! log_timed_trace {
62 ($msg:expr, $code:block) => {
64 $crate::log_timed!($crate::interface::logging::LogLevel::Trace, $msg, $code)
65 };
66
67 ($fmt:expr, $($arg:tt)*; $code:block) => {
69 $crate::log_timed!($crate::interface::logging::LogLevel::Trace, $fmt, $($arg)*; $code)
70 };
71}
72
73#[macro_export]
75macro_rules! log_timed_debug {
76 ($msg:expr, $code:block) => {
78 $crate::log_timed!($crate::interface::logging::LogLevel::Debug, $msg, $code)
79 };
80
81 ($fmt:expr, $($arg:tt)*; $code:block) => {
83 $crate::log_timed!($crate::interface::logging::LogLevel::Debug, $fmt, $($arg)*; $code)
84 };
85}
86
87#[macro_export]
89macro_rules! log_timed_info {
90 ($msg:expr, $code:block) => {
92 $crate::log_timed!($crate::interface::logging::LogLevel::Info, $msg, $code)
93 };
94
95 ($fmt:expr, $($arg:tt)*; $code:block) => {
97 $crate::log_timed!($crate::interface::logging::LogLevel::Info, $fmt, $($arg)*; $code)
98 };
99}
100
101#[macro_export]
103macro_rules! log_timed_warn {
104 ($msg:expr, $code:block) => {
106 $crate::log_timed!($crate::interface::logging::LogLevel::Warn, $msg, $code)
107 };
108
109 ($fmt:expr, $($arg:tt)*; $code:block) => {
111 $crate::log_timed!($crate::interface::logging::LogLevel::Warn, $fmt, $($arg)*; $code)
112 };
113}
114
115#[macro_export]
117macro_rules! log_timed_error {
118 ($msg:expr, $code:block) => {
120 $crate::log_timed!($crate::interface::logging::LogLevel::Error, $msg, $code)
121 };
122
123 ($fmt:expr, $($arg:tt)*; $code:block) => {
125 $crate::log_timed!($crate::interface::logging::LogLevel::Error, $fmt, $($arg)*; $code)
126 };
127}
128
129#[macro_export]
131macro_rules! log_timed_critical {
132 ($msg:expr, $code:block) => {
134 $crate::log_timed!($crate::interface::logging::LogLevel::Critical, $msg, $code)
135 };
136
137 ($fmt:expr, $($arg:tt)*; $code:block) => {
139 $crate::log_timed!($crate::interface::logging::LogLevel::Critical, $fmt, $($arg)*; $code)
140 };
141}
142
143#[cfg(test)]
144mod tests {
145 use crossbeam_channel::unbounded;
146
147 use super::*;
148 use crate::{
149 interface::logging::{LogLevel, mock::with_mock_logger},
150 log_timed_debug, log_timed_info, log_timed_trace,
151 };
152
153 #[test]
154 fn test_format_duration_nanos() {
155 assert_eq!(format_duration_nanos(500), "500ns");
156 assert_eq!(format_duration_nanos(1_500), "1.50μs");
157 assert_eq!(format_duration_nanos(1_500_000), "1.50ms");
158 assert_eq!(format_duration_nanos(1_500_000_000), "1.50s");
159 }
160
161 #[test]
162 fn test_simple_timed_log() {
163 let (sender, receiver) = unbounded();
164
165 with_mock_logger(sender, || {
166 let result = log_timed_debug!("Test operation", {
167 std::thread::sleep(std::time::Duration::from_millis(10));
168 42
169 });
170 assert_eq!(result, 42);
171 });
172
173 let record = receiver.try_recv().unwrap();
174 assert_eq!(record.level, LogLevel::Debug);
175 assert!(record.message.starts_with("Test operation (took "));
176 assert!(record.message.ends_with(")"));
177 }
178
179 #[test]
180 fn test_timed_log_with_inline_variables() {
181 let (sender, receiver) = unbounded();
182
183 with_mock_logger(sender, || {
184 let operation = "database init";
185 let result = log_timed_info!("Performing {operation}", {
186 std::thread::sleep(std::time::Duration::from_millis(5));
187 "success"
188 });
189 assert_eq!(result, "success");
190 });
191
192 let record = receiver.try_recv().unwrap();
193 assert_eq!(record.level, LogLevel::Info);
194 assert!(record.message.starts_with("Performing database init (took "));
195 }
196
197 #[test]
198 fn test_timed_log_returns_result() {
199 let (sender, _receiver) = unbounded();
200
201 with_mock_logger(sender, || {
202 let value = log_timed_debug!("Computing value", { 100 + 200 });
205 assert_eq!(value, 300);
206
207 let vec = log_timed_info!("Creating vector", { vec![1, 2, 3, 4, 5] });
209 assert_eq!(vec.len(), 5);
210 });
211 }
212
213 #[test]
214 fn test_all_timed_log_levels() {
215 let (sender, receiver) = unbounded();
216
217 with_mock_logger(sender, || {
218 log_timed_trace!("Trace operation", { 1 });
219 log_timed_debug!("Debug operation", { 2 });
220 log_timed_info!("Info operation", { 3 });
221 log_timed_warn!("Warn operation", { 4 });
222 log_timed_error!("Error operation", { 5 });
223 log_timed_critical!("Critical operation", { 6 });
224 });
225
226 let logs: Vec<_> = (0..6).map(|_| receiver.try_recv().unwrap()).collect();
227
228 assert_eq!(logs[0].level, LogLevel::Trace);
229 assert!(logs[0].message.starts_with("Trace operation (took "));
230
231 assert_eq!(logs[1].level, LogLevel::Debug);
232 assert!(logs[1].message.starts_with("Debug operation (took "));
233
234 assert_eq!(logs[2].level, LogLevel::Info);
235 assert!(logs[2].message.starts_with("Info operation (took "));
236
237 assert_eq!(logs[3].level, LogLevel::Warn);
238 assert!(logs[3].message.starts_with("Warn operation (took "));
239
240 assert_eq!(logs[4].level, LogLevel::Error);
241 assert!(logs[4].message.starts_with("Error operation (took "));
242
243 assert_eq!(logs[5].level, LogLevel::Critical);
244 assert!(logs[5].message.starts_with("Critical operation (took "));
245 }
246}