use crate::constants::{ACCESS_LOG_FILE, SYSTEM_LOG_FILE}; use rpxy_lib::log_event_names; use std::str::FromStr; use tracing_subscriber::{filter::filter_fn, fmt, prelude::*}; #[allow(unused)] pub use tracing::{debug, error, info, warn}; /// Initialize the logger with the RUST_LOG environment variable. pub fn init_logger(log_dir_path: Option<&str>) { let level = std::env::var("RUST_LOG") .ok() .and_then(|s| tracing::Level::from_str(&s).ok()) .unwrap_or(tracing::Level::INFO); match log_dir_path { None => init_stdio_logger(level), Some(path) => init_file_logger(level, path), } } /// file logging fn init_file_logger(level: tracing::Level, log_dir_path: &str) { println!("Activate logging to files: {}", log_dir_path); let log_dir = std::path::Path::new(log_dir_path); if !log_dir.exists() { println!("Directory does not exist, creating: {}", log_dir.display()); std::fs::create_dir_all(log_dir).expect("Failed to create log directory"); } let access_log_path = log_dir.join(ACCESS_LOG_FILE); let system_log_path = log_dir.join(SYSTEM_LOG_FILE); println!("Access log: {}", access_log_path.display()); println!("System and error log: {}", system_log_path.display()); let access_log = open_log_file(&access_log_path); let system_log = open_log_file(&system_log_path); let access_layer = fmt::layer() .with_line_number(false) .with_thread_ids(false) .with_thread_names(false) .with_target(false) .with_level(false) .compact() .with_ansi(false) .with_writer(access_log) .with_filter(AccessLogFilter); let system_layer = fmt::layer() .with_line_number(false) .with_thread_ids(false) .with_thread_names(false) .with_target(false) .with_level(true) .compact() .with_ansi(false) .with_writer(system_log) .with_filter(filter_fn(move |metadata| { (is_cargo_pkg(metadata) && metadata.name() != log_event_names::ACCESS_LOG && metadata.level() <= &level) || metadata.level() <= &tracing::Level::WARN.min(level) })); tracing_subscriber::registry().with(access_layer).with(system_layer).init(); } /// stdio logging fn init_stdio_logger(level: tracing::Level) { // This limits the logger to emit only this crate with any level above RUST_LOG, // for included crates it will emit only ERROR (in prod)/INFO (in dev) or above level. let base_layer = fmt::layer().with_level(true).with_thread_ids(false); let debug = level > tracing::Level::INFO; let filter = filter_fn(move |metadata| { if debug { (is_cargo_pkg(metadata) && metadata.level() <= &level) || metadata.level() <= &tracing::Level::INFO.min(level) } else { (is_cargo_pkg(metadata) && metadata.level() <= &level) || metadata.level() <= &tracing::Level::WARN.min(level) } }); let stdio_layer = if debug { base_layer .with_line_number(true) .with_target(true) .with_thread_names(true) .with_target(true) .compact() .with_filter(filter) } else { base_layer.with_target(false).compact().with_filter(filter) }; tracing_subscriber::registry().with(stdio_layer).init(); } /// Access log filter struct AccessLogFilter; impl tracing_subscriber::layer::Filter for AccessLogFilter { fn enabled(&self, metadata: &tracing::Metadata<'_>, _: &tracing_subscriber::layer::Context<'_, S>) -> bool { is_cargo_pkg(metadata) && metadata.name().contains(log_event_names::ACCESS_LOG) && metadata.level() <= &tracing::Level::INFO } } #[inline] /// Create a file for logging fn open_log_file

(path: P) -> std::fs::File where P: AsRef, { // create a file if it does not exist std::fs::OpenOptions::new() .create(true) .append(true) .open(path) .expect("Failed to open the log file") } #[inline] /// Matches cargo package name with `_` instead of `-` fn is_cargo_pkg(metadata: &tracing::Metadata<'_>) -> bool { let pkg_name = env!("CARGO_PKG_NAME").replace('-', "_"); metadata.target().starts_with(&pkg_name) }