1pub mod config;
49pub mod context;
50pub mod error;
51pub mod formatter;
52pub mod formatters;
53pub mod handler;
54pub mod integration;
55pub mod level;
56pub mod logger;
57#[doc(hidden)]
58pub mod macros;
59pub mod record;
60pub mod scope;
61pub mod test_utils;
62
63pub use config::{LoggerConfig, LoggerConfigBuilder};
64pub use error::{error_chain, install_panic_hook, ContextError, OptionExt, ResultExt};
65pub use formatters::json::JsonFormatter;
66pub use formatters::template::TemplateFormatter;
67pub use formatters::text::TextFormatter;
68pub use formatters::FormatterTrait;
69pub use handler::Handler;
70pub use level::LogLevel;
71pub use logger::{global, init, log, Logger};
72pub use record::Record;
73pub use scope::{ScopeError, ScopeGuard};
74
75pub use log::{LevelFilter, Log, Metadata, Record as LogRecord};
77
78use crossbeam_channel::{bounded, Receiver, Sender};
80use parking_lot::RwLock as PLRwLock;
81use std::sync::atomic::{AtomicBool, Ordering};
82use std::sync::Arc;
83use std::thread;
84use std::time::Duration;
85
86#[allow(dead_code)]
88pub static STATIC_LEVEL: level::LogLevel = level::LogLevel::Trace;
89
90#[doc(hidden)]
92pub enum AsyncLogCommand {
93 Log(Record),
95 Shutdown,
97}
98
99#[derive(Clone, Debug)]
101pub struct AsyncLoggerHandle {
102 sender: Sender<AsyncLogCommand>,
104 running: Arc<AtomicBool>,
106}
107
108impl AsyncLoggerHandle {
109 pub fn log(&self, record: Record) -> bool {
111 if !self.running.load(Ordering::Relaxed) {
112 return false;
113 }
114
115 self.sender.try_send(AsyncLogCommand::Log(record)).is_ok()
116 }
117
118 pub fn shutdown(&self) {
120 if !self.running.load(Ordering::Relaxed) {
121 return;
122 }
123
124 let _ = self.sender.send(AsyncLogCommand::Shutdown);
126 self.running.store(false, Ordering::Relaxed);
127 }
128}
129
130impl Drop for AsyncLoggerHandle {
131 fn drop(&mut self) {
132 self.shutdown();
133 }
134}
135
136pub struct AsyncLoggerBuilder {
138 queue_size: usize,
140 handlers: Vec<Arc<PLRwLock<dyn Handler>>>,
142 level: LogLevel,
144 workers: usize,
146}
147
148impl AsyncLoggerBuilder {
149 pub fn new() -> Self {
151 Self {
152 queue_size: 10000,
153 handlers: Vec::new(),
154 level: LogLevel::Info,
155 workers: 1,
156 }
157 }
158
159 pub fn with_queue_size(mut self, queue_size: usize) -> Self {
161 self.queue_size = queue_size;
162 self
163 }
164
165 pub fn with_handlers(mut self, handlers: Vec<Arc<PLRwLock<dyn Handler>>>) -> Self {
167 self.handlers = handlers;
168 self
169 }
170
171 pub fn with_level(mut self, level: LogLevel) -> Self {
173 self.level = level;
174 self
175 }
176
177 pub fn with_workers(mut self, workers: usize) -> Self {
179 self.workers = workers;
180 self
181 }
182
183 pub fn build(self) -> AsyncLoggerHandle {
185 let (sender, receiver) = bounded(self.queue_size);
187
188 let running = Arc::new(AtomicBool::new(true));
190 let running_clone = running.clone();
191
192 let handlers = self.handlers.clone();
194 let level = self.level;
195
196 for _ in 0..self.workers {
198 let receiver = receiver.clone();
199 let handlers = handlers.clone();
200 let running = running_clone.clone();
201
202 thread::spawn(move || {
203 Self::worker_thread(receiver, handlers, level, running);
204 });
205 }
206
207 AsyncLoggerHandle { sender, running }
209 }
210
211 fn worker_thread(
213 receiver: Receiver<AsyncLogCommand>,
214 handlers: Vec<Arc<PLRwLock<dyn Handler>>>,
215 level: LogLevel,
216 running: Arc<AtomicBool>,
217 ) {
218 while running.load(Ordering::Relaxed) {
219 match receiver.recv_timeout(Duration::from_millis(100)) {
220 Ok(AsyncLogCommand::Log(record)) => {
221 if record.level() >= level {
223 for handler in &handlers {
224 let guard = handler.write();
225 if guard.is_enabled() && record.level() >= guard.level() {
226 let _ = guard.handle(&record);
227 }
228 }
229 }
230 }
231 Ok(AsyncLogCommand::Shutdown) => {
232 running.store(false, Ordering::Relaxed);
233 break;
234 }
235 Err(crossbeam_channel::RecvTimeoutError::Timeout) => {
236 continue;
238 }
239 Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
240 running.store(false, Ordering::Relaxed);
242 break;
243 }
244 }
245 }
246 }
247}
248
249impl Default for AsyncLoggerBuilder {
250 fn default() -> Self {
251 Self::new()
252 }
253}
254
255pub mod log_adapter {
257 use crate::level::LogLevel;
258 use crate::logger::global;
259 use crate::record::Record as LoguruRecord;
260 use log::{Level, Log, Metadata, Record};
261
262 pub struct LogAdapter;
264
265 impl Log for LogAdapter {
266 fn enabled(&self, metadata: &Metadata) -> bool {
267 let level = match metadata.level() {
268 Level::Error => LogLevel::Error,
269 Level::Warn => LogLevel::Warning,
270 Level::Info => LogLevel::Info,
271 Level::Debug => LogLevel::Debug,
272 Level::Trace => LogLevel::Trace,
273 };
274
275 level >= global().read().level()
276 }
277
278 fn log(&self, record: &Record) {
279 if !self.enabled(record.metadata()) {
280 return;
281 }
282
283 let level = match record.level() {
284 Level::Error => LogLevel::Error,
285 Level::Warn => LogLevel::Warning,
286 Level::Info => LogLevel::Info,
287 Level::Debug => LogLevel::Debug,
288 Level::Trace => LogLevel::Trace,
289 };
290
291 let loguru_record = LoguruRecord::new(
292 level,
293 record.args().to_string(),
294 record.module_path().map(|s| s.to_string()),
295 record.file().map(|s| s.to_string()),
296 record.line(),
297 );
298
299 let _ = global().read().log(&loguru_record);
300 }
301
302 fn flush(&self) {
303 }
305 }
306
307 pub fn init() -> Result<(), log::SetLoggerError> {
309 static LOGGER: LogAdapter = LogAdapter;
310 log::set_logger(&LOGGER)?;
311 Ok(())
312 }
313
314 pub fn set_max_level(level: LogLevel) {
316 let max_level = match level {
317 LogLevel::Error => log::LevelFilter::Error,
318 LogLevel::Warning => log::LevelFilter::Warn,
319 LogLevel::Info => log::LevelFilter::Info,
320 LogLevel::Debug => log::LevelFilter::Debug,
321 LogLevel::Trace => log::LevelFilter::Trace,
322 _ => log::LevelFilter::Off,
323 };
324
325 log::set_max_level(max_level);
326 }
327}
328
329pub mod compile_time {
331 use crate::level::LogLevel;
332
333 #[macro_export]
335 macro_rules! compile_time_level_enabled {
336 ($level:expr) => {{
337 #[cfg(feature = "max_level_off")]
338 {
339 false
340 }
341 #[cfg(not(feature = "max_level_off"))]
342 {
343 #[cfg(feature = "max_level_error")]
344 {
345 $level >= $crate::LogLevel::Error
346 }
347 #[cfg(feature = "max_level_warn")]
348 {
349 $level >= $crate::LogLevel::Warning
350 }
351 #[cfg(feature = "max_level_info")]
352 {
353 $level >= $crate::LogLevel::Info
354 }
355 #[cfg(feature = "max_level_debug")]
356 {
357 $level >= $crate::LogLevel::Debug
358 }
359 #[cfg(feature = "max_level_trace")]
360 {
361 $level >= $crate::LogLevel::Trace
362 }
363 #[cfg(not(any(
364 feature = "max_level_error",
365 feature = "max_level_warn",
366 feature = "max_level_info",
367 feature = "max_level_debug",
368 feature = "max_level_trace"
369 )))]
370 {
371 true
372 }
373 }
374 }};
375 }
376
377 #[inline]
379 pub fn runtime_level_enabled(level: LogLevel, current_level: LogLevel) -> bool {
380 level >= current_level
381 }
382}
383
384pub mod benchmark {
386 use crate::level::LogLevel;
387 use crate::logger::Logger;
388 use crate::record::Record;
389 use std::time::Instant;
390
391 pub fn measure_throughput(logger: &Logger, iterations: usize, level: LogLevel) -> f64 {
393 let start = Instant::now();
394
395 for i in 0..iterations {
396 let record = Record::new(
397 level,
398 format!("Benchmark message {}", i),
399 Some("benchmark".to_string()),
400 Some("benchmark.rs".to_string()),
401 Some(1),
402 );
403 let _ = logger.log(&record);
404 }
405
406 let elapsed = start.elapsed();
407 iterations as f64 / elapsed.as_secs_f64()
408 }
409
410 pub fn compare_sync_vs_async(
412 logger: &mut Logger,
413 iterations: usize,
414 level: LogLevel,
415 ) -> (f64, f64) {
416 let sync_throughput = measure_throughput(logger, iterations, level);
418
419 logger.set_async(true, Some(iterations));
421
422 let async_throughput = measure_throughput(logger, iterations, level);
424
425 logger.set_async(false, None);
427
428 (sync_throughput, async_throughput)
429 }
430}