use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};
use tracing::debug;
use tracing_subscriber::EnvFilter;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoggingSettings {
pub filter: String,
pub ansi: bool,
pub with_timestamp: bool,
pub source: String,
}
#[derive(Deserialize, Default)]
struct LoggingConfig {
level: Option<String>,
ansi: Option<bool>,
filter: Option<String>,
format: Option<FormatConfig>,
}
#[derive(Deserialize, Default)]
struct FormatConfig {
with_timestamp: Option<bool>,
}
fn select_config(preferred: &Path) -> (LoggingConfig, String) {
let candidate = preferred.join("logging.yml");
if candidate.exists() {
match fs::read_to_string(&candidate) {
Ok(txt) => (
serde_yaml::from_str(&txt).unwrap_or_default(),
candidate.display().to_string(),
),
Err(err) => {
debug!(path=%candidate.display(), error=%err, "Failed to read logging.yml; using defaults");
(
LoggingConfig::default(),
format!("{} (read error, using defaults)", candidate.display()),
)
}
}
} else {
let cwd = PathBuf::from("logging.yml");
if cwd.exists() {
match fs::read_to_string(&cwd) {
Ok(txt) => (
serde_yaml::from_str(&txt).unwrap_or_default(),
cwd.display().to_string(),
),
Err(err) => {
debug!(path=%cwd.display(), error=%err, "Failed to read cwd logging.yml; using defaults");
(
LoggingConfig::default(),
format!("{} (read error, using defaults)", cwd.display()),
)
}
}
} else {
(LoggingConfig::default(), "default".to_string())
}
}
}
pub fn load_logging_settings(preferred: &Path) -> LoggingSettings {
let (raw_cfg, source) = select_config(preferred);
let filter = raw_cfg
.filter
.unwrap_or_else(|| raw_cfg.level.unwrap_or_else(|| "info".to_string()));
let ansi = raw_cfg.ansi.unwrap_or(true);
let with_timestamp = raw_cfg
.format
.as_ref()
.and_then(|f| f.with_timestamp)
.unwrap_or(true);
LoggingSettings {
filter,
ansi,
with_timestamp,
source,
}
}
pub fn init_from_dir(preferred: &Path) {
let settings = load_logging_settings(preferred);
let subscriber = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::new(settings.filter.clone()))
.with_ansi(settings.ansi);
let result = if settings.with_timestamp {
subscriber.try_init()
} else {
subscriber.without_time().try_init()
};
if result.is_ok() {
debug!(target="allora::logging", source=%settings.source, filter=%settings.filter, timestamp=%settings.with_timestamp, ansi=%settings.ansi, "Logging initialized");
} else {
debug!(target="allora::logging", wanted_filter=%settings.filter, "Logging subscriber already set; using existing configuration");
}
}