1use std::env;
2use std::path::PathBuf;
3use tracing_appender::non_blocking::WorkerGuard;
4use tracing_subscriber::EnvFilter;
5use tracing_subscriber::fmt;
6use tracing_subscriber::prelude::*;
7
8const DEFAULT_LOG_DIR: &str = "./logs";
9const ENV_LOG_DIR: &str = "EVO_LOG_DIR";
10
11pub fn log_dir() -> PathBuf {
12 env::var(ENV_LOG_DIR)
13 .map(PathBuf::from)
14 .unwrap_or_else(|_| PathBuf::from(DEFAULT_LOG_DIR))
15}
16
17pub fn init_logging(component: &str) -> WorkerGuard {
18 let dir = log_dir();
19 std::fs::create_dir_all(&dir).expect("Failed to create log directory");
20
21 let file_appender = tracing_appender::rolling::daily(&dir, format!("{component}.log"));
22 let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
23
24 let file_layer = fmt::layer()
25 .json()
26 .with_writer(non_blocking)
27 .with_target(true)
28 .with_thread_ids(true)
29 .with_file(true)
30 .with_line_number(true);
31
32 let stdout_layer = fmt::layer().with_target(true).with_thread_ids(false);
33
34 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
35
36 tracing_subscriber::registry()
37 .with(filter)
38 .with(file_layer)
39 .with(stdout_layer)
40 .init();
41
42 guard
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn default_log_dir() {
51 unsafe { env::remove_var(ENV_LOG_DIR) };
52 assert_eq!(log_dir(), PathBuf::from("./logs"));
53 }
54
55 #[test]
56 fn custom_log_dir() {
57 unsafe { env::set_var(ENV_LOG_DIR, "/tmp/evo-test-logs") };
58 assert_eq!(log_dir(), PathBuf::from("/tmp/evo-test-logs"));
59 unsafe { env::remove_var(ENV_LOG_DIR) };
60 }
61}