pub mod config;
pub mod context;
pub mod error;
pub mod formatter;
pub mod formatters;
pub mod handler;
pub mod integration;
pub mod level;
pub mod logger;
#[doc(hidden)]
pub mod macros;
pub mod record;
pub mod record_pool;
pub mod scope;
pub mod test_utils;
pub use config::{LoggerConfig, LoggerConfigBuilder};
pub use error::{error_chain, install_panic_hook, ContextError, OptionExt, ResultExt};
pub use formatters::json::JsonFormatter;
pub use formatters::template::TemplateFormatter;
pub use formatters::text::TextFormatter;
pub use formatters::FormatterTrait;
pub use handler::Handler;
pub use level::LogLevel;
pub use logger::{global, init, log, Logger};
pub use record::Record;
pub use record_pool::{PooledRecord, RecordPool};
pub use scope::{ScopeError, ScopeGuard};
pub use log::{LevelFilter, Log, Metadata, Record as LogRecord};
use crossbeam_channel::{bounded, Receiver, Sender};
use parking_lot::RwLock as PLRwLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
#[allow(dead_code)]
pub static STATIC_LEVEL: level::LogLevel = level::LogLevel::Trace;
#[doc(hidden)]
pub enum AsyncLogCommand {
Log(Record),
Shutdown,
}
#[derive(Clone, Debug)]
pub struct AsyncLoggerHandle {
sender: Sender<AsyncLogCommand>,
running: Arc<AtomicBool>,
}
impl AsyncLoggerHandle {
pub fn log(&self, record: Record) -> bool {
if !self.running.load(Ordering::Relaxed) {
return false;
}
self.sender.try_send(AsyncLogCommand::Log(record)).is_ok()
}
pub fn shutdown(&self) {
if !self.running.load(Ordering::Relaxed) {
return;
}
let _ = self.sender.send(AsyncLogCommand::Shutdown);
self.running.store(false, Ordering::Relaxed);
}
}
impl Drop for AsyncLoggerHandle {
fn drop(&mut self) {
self.shutdown();
}
}
pub struct AsyncLoggerBuilder {
queue_size: usize,
handlers: Vec<Arc<PLRwLock<dyn Handler>>>,
level: LogLevel,
workers: usize,
}
impl AsyncLoggerBuilder {
pub fn new() -> Self {
Self {
queue_size: 10000,
handlers: Vec::new(),
level: LogLevel::Info,
workers: 1,
}
}
pub fn with_queue_size(mut self, queue_size: usize) -> Self {
self.queue_size = queue_size;
self
}
pub fn with_handlers(mut self, handlers: Vec<Arc<PLRwLock<dyn Handler>>>) -> Self {
self.handlers = handlers;
self
}
pub fn with_level(mut self, level: LogLevel) -> Self {
self.level = level;
self
}
pub fn with_workers(mut self, workers: usize) -> Self {
self.workers = workers;
self
}
pub fn build(self) -> AsyncLoggerHandle {
let (sender, receiver) = bounded(self.queue_size);
let running = Arc::new(AtomicBool::new(true));
let running_clone = running.clone();
let handlers = self.handlers.clone();
let level = self.level;
for _ in 0..self.workers {
let receiver = receiver.clone();
let handlers = handlers.clone();
let running = running_clone.clone();
thread::spawn(move || {
Self::worker_thread(receiver, handlers, level, running);
});
}
AsyncLoggerHandle { sender, running }
}
fn worker_thread(
receiver: Receiver<AsyncLogCommand>,
handlers: Vec<Arc<PLRwLock<dyn Handler>>>,
level: LogLevel,
running: Arc<AtomicBool>,
) {
while running.load(Ordering::Relaxed) {
match receiver.recv_timeout(Duration::from_millis(100)) {
Ok(AsyncLogCommand::Log(record)) => {
if record.level() >= level {
for handler in &handlers {
let guard = handler.write();
if guard.is_enabled() && record.level() >= guard.level() {
let _ = guard.handle(&record);
}
}
}
}
Ok(AsyncLogCommand::Shutdown) => {
running.store(false, Ordering::Relaxed);
break;
}
Err(crossbeam_channel::RecvTimeoutError::Timeout) => {
continue;
}
Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
running.store(false, Ordering::Relaxed);
break;
}
}
}
}
}
impl Default for AsyncLoggerBuilder {
fn default() -> Self {
Self::new()
}
}
pub mod log_adapter {
use crate::level::LogLevel;
use crate::logger::global;
use crate::record::Record as LoguruRecord;
use log::{Level, Log, Metadata, Record};
pub struct LogAdapter;
impl Log for LogAdapter {
fn enabled(&self, metadata: &Metadata) -> bool {
let level = match metadata.level() {
Level::Error => LogLevel::Error,
Level::Warn => LogLevel::Warning,
Level::Info => LogLevel::Info,
Level::Debug => LogLevel::Debug,
Level::Trace => LogLevel::Trace,
};
level >= global().read().level()
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let level = match record.level() {
Level::Error => LogLevel::Error,
Level::Warn => LogLevel::Warning,
Level::Info => LogLevel::Info,
Level::Debug => LogLevel::Debug,
Level::Trace => LogLevel::Trace,
};
let loguru_record = LoguruRecord::new(
level,
record.args().to_string(),
record.module_path().map(|s| s.to_string()),
record.file().map(|s| s.to_string()),
record.line(),
);
let _ = global().read().log(&loguru_record);
}
fn flush(&self) {
}
}
pub fn init() -> Result<(), log::SetLoggerError> {
static LOGGER: LogAdapter = LogAdapter;
log::set_logger(&LOGGER)?;
Ok(())
}
pub fn set_max_level(level: LogLevel) {
let max_level = match level {
LogLevel::Error => log::LevelFilter::Error,
LogLevel::Warning => log::LevelFilter::Warn,
LogLevel::Info => log::LevelFilter::Info,
LogLevel::Debug => log::LevelFilter::Debug,
LogLevel::Trace => log::LevelFilter::Trace,
_ => log::LevelFilter::Off,
};
log::set_max_level(max_level);
}
}
pub mod compile_time {
use crate::level::LogLevel;
#[macro_export]
macro_rules! compile_time_level_enabled {
($level:expr) => {{
#[cfg(feature = "max_level_off")]
{
false
}
#[cfg(not(feature = "max_level_off"))]
{
#[cfg(feature = "max_level_error")]
{
$level >= $crate::LogLevel::Error
}
#[cfg(feature = "max_level_warn")]
{
$level >= $crate::LogLevel::Warning
}
#[cfg(feature = "max_level_info")]
{
$level >= $crate::LogLevel::Info
}
#[cfg(feature = "max_level_debug")]
{
$level >= $crate::LogLevel::Debug
}
#[cfg(feature = "max_level_trace")]
{
$level >= $crate::LogLevel::Trace
}
#[cfg(not(any(
feature = "max_level_error",
feature = "max_level_warn",
feature = "max_level_info",
feature = "max_level_debug",
feature = "max_level_trace"
)))]
{
true
}
}
}};
}
#[inline]
pub fn runtime_level_enabled(level: LogLevel, current_level: LogLevel) -> bool {
level >= current_level
}
}
pub mod benchmark {
use crate::level::LogLevel;
use crate::logger::Logger;
use crate::record::Record;
use std::time::Instant;
pub fn measure_throughput(logger: &Logger, iterations: usize, level: LogLevel) -> f64 {
let start = Instant::now();
for i in 0..iterations {
let record = Record::new(
level,
format!("Benchmark message {}", i),
Some("benchmark".to_string()),
Some("benchmark.rs".to_string()),
Some(1),
);
let _ = logger.log(&record);
}
let elapsed = start.elapsed();
iterations as f64 / elapsed.as_secs_f64()
}
pub fn compare_sync_vs_async(
logger: &mut Logger,
iterations: usize,
level: LogLevel,
) -> (f64, f64) {
let sync_throughput = measure_throughput(logger, iterations, level);
logger.set_async(true, Some(iterations));
let async_throughput = measure_throughput(logger, iterations, level);
logger.set_async(false, None);
(sync_throughput, async_throughput)
}
}