use tracing::Level;
use tracing_subscriber::{
EnvFilter,
fmt::{self, format::FmtSpan},
layer::SubscriberExt,
util::SubscriberInitExt,
};
#[derive(Debug, Clone)]
pub struct LoggingConfig {
pub level: LogLevel,
pub with_timestamps: bool,
pub with_thread_ids: bool,
pub with_source_location: bool,
pub with_span_events: bool,
pub json_format: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
impl LogLevel {
fn to_tracing_level(&self) -> Level {
match self {
LogLevel::Trace => Level::TRACE,
LogLevel::Debug => Level::DEBUG,
LogLevel::Info => Level::INFO,
LogLevel::Warn => Level::WARN,
LogLevel::Error => Level::ERROR,
}
}
}
impl Default for LoggingConfig {
fn default() -> Self {
Self {
level: LogLevel::Info,
with_timestamps: true,
with_thread_ids: false,
with_source_location: false,
with_span_events: false,
json_format: false,
}
}
}
impl LoggingConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_level(mut self, level: LogLevel) -> Self {
self.level = level;
self
}
pub fn with_timestamps(mut self, enable: bool) -> Self {
self.with_timestamps = enable;
self
}
pub fn with_thread_ids(mut self, enable: bool) -> Self {
self.with_thread_ids = enable;
self
}
pub fn with_source_location(mut self, enable: bool) -> Self {
self.with_source_location = enable;
self
}
pub fn with_span_events(mut self, enable: bool) -> Self {
self.with_span_events = enable;
self
}
pub fn with_json_format(mut self, enable: bool) -> Self {
self.json_format = enable;
self
}
pub fn development() -> Self {
Self {
level: LogLevel::Debug,
with_timestamps: true,
with_thread_ids: true,
with_source_location: true,
with_span_events: true,
json_format: false,
}
}
pub fn production() -> Self {
Self {
level: LogLevel::Info,
with_timestamps: true,
with_thread_ids: false,
with_source_location: false,
with_span_events: false,
json_format: true, }
}
}
pub fn init_logging(config: LoggingConfig) {
let env_filter = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new(config.level.to_tracing_level().as_str()))
.unwrap_or_else(|_| EnvFilter::new("info"));
let span_events = if config.with_span_events {
FmtSpan::ENTER | FmtSpan::CLOSE
} else {
FmtSpan::NONE
};
if config.json_format {
let fmt_layer = fmt::layer()
.json()
.with_span_events(span_events)
.with_current_span(true)
.with_thread_ids(config.with_thread_ids)
.with_file(config.with_source_location)
.with_line_number(config.with_source_location);
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.init();
} else {
let fmt_layer = fmt::layer()
.with_span_events(span_events)
.with_thread_ids(config.with_thread_ids)
.with_file(config.with_source_location)
.with_line_number(config.with_source_location)
.with_target(config.with_source_location);
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.init();
}
}
pub fn init_default_logging() {
init_logging(LoggingConfig::default());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logging_config_default() {
let config = LoggingConfig::default();
assert_eq!(config.level, LogLevel::Info);
assert!(config.with_timestamps);
assert!(!config.with_thread_ids);
}
#[test]
fn test_logging_config_development() {
let config = LoggingConfig::development();
assert_eq!(config.level, LogLevel::Debug);
assert!(config.with_span_events);
assert!(!config.json_format);
}
#[test]
fn test_logging_config_production() {
let config = LoggingConfig::production();
assert_eq!(config.level, LogLevel::Info);
assert!(config.json_format);
assert!(!config.with_source_location);
}
#[test]
fn test_logging_config_builder() {
let config = LoggingConfig::new()
.with_level(LogLevel::Trace)
.with_timestamps(false)
.with_thread_ids(true)
.with_json_format(true);
assert_eq!(config.level, LogLevel::Trace);
assert!(!config.with_timestamps);
assert!(config.with_thread_ids);
assert!(config.json_format);
}
}