use std::sync::Once;
use tracing::Level;
use tracing_appender::rolling;
use tracing_subscriber::{
fmt::{self, format::FmtSpan},
prelude::*,
registry, EnvFilter,
};
static INIT: Once = Once::new();
#[derive(Clone, Debug)]
pub struct LogConfig {
pub app_name: String,
pub log_level: Level,
pub json_format: bool,
pub log_dir: Option<String>,
pub log_to_stdout: bool,
}
impl Default for LogConfig {
fn default() -> Self {
Self {
app_name: "network-protocol".to_string(),
log_level: Level::INFO,
json_format: false,
log_dir: None,
log_to_stdout: true,
}
}
}
pub fn init_logging(config: &LogConfig) {
INIT.call_once(|| {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new(format!(
"{},{app_name}={level}",
std::env::var("RUST_LOG").unwrap_or_default(),
app_name = config.app_name,
level = config.log_level
))
});
let registry = registry().with(filter);
match (&config.log_dir, config.log_to_stdout) {
(Some(log_dir), true) => {
let file_appender = rolling::daily(log_dir, format!("{}.log", config.app_name));
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
if config.json_format {
let file_layer = fmt::layer()
.json()
.with_writer(non_blocking)
.with_span_events(FmtSpan::CLOSE);
let stdout_layer = fmt::layer().with_writer(std::io::stdout).with_ansi(true);
registry.with(file_layer).with(stdout_layer).init();
} else {
let file_layer = fmt::layer()
.with_writer(non_blocking)
.with_span_events(FmtSpan::CLOSE);
let stdout_layer = fmt::layer().with_writer(std::io::stdout).with_ansi(true);
registry.with(file_layer).with(stdout_layer).init();
}
}
(Some(log_dir), false) => {
let file_appender = rolling::daily(log_dir, format!("{}.log", config.app_name));
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
if config.json_format {
let file_layer = fmt::layer()
.json()
.with_writer(non_blocking)
.with_span_events(FmtSpan::CLOSE);
registry.with(file_layer).init();
} else {
let file_layer = fmt::layer()
.with_writer(non_blocking)
.with_span_events(FmtSpan::CLOSE);
registry.with(file_layer).init();
}
}
(None, true) => {
if config.json_format {
let stdout_layer = fmt::layer()
.json()
.with_writer(std::io::stdout)
.with_span_events(FmtSpan::CLOSE);
registry.with(stdout_layer).init();
} else {
let stdout_layer = fmt::layer()
.with_writer(std::io::stdout)
.with_ansi(true)
.with_span_events(FmtSpan::CLOSE);
registry.with(stdout_layer).init();
}
}
(None, false) => {
let stdout_layer = fmt::layer().with_writer(std::io::stdout).with_ansi(true);
registry.with(stdout_layer).init();
tracing::warn!("No log output configured, defaulting to stdout");
}
}
tracing::info!("Logging initialized at {} level", config.log_level);
});
}
pub fn setup_default_logging() {
init_logging(&LogConfig::default());
}