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