1use std::path::PathBuf;
2
3use anyhow::Result;
4
5use config::Config;
6
7use serde::{Deserialize, Serialize};
8
9use tracing_subscriber::util::SubscriberInitExt;
10use tracing_subscriber::{EnvFilter, FmtSubscriber};
11
12const DEFAULT_TRACKED_CRATES: &[&str] = &[
13 "menmosd",
14 "amphora",
15 "xecute",
16 "rapidquery",
17 "antidns",
18 "lfan",
19 "apikit",
20 "menmos_auth",
21 "menmos_client",
22 "menmos_protocol",
23 "betterstreams",
24 "repository",
25 "menmos-std",
26 "tower_http",
27];
28
29#[cfg(debug_assertions)]
30const NORMAL_CRATE_LEVEL: &str = "debug";
31
32#[cfg(not(debug_assertions))]
33const NORMAL_CRATE_LEVEL: &str = "info";
34
35#[cfg(debug_assertions)]
36const DETAILED_CRATE_LEVEL: &str = "trace";
37
38#[cfg(not(debug_assertions))]
39const DETAILED_CRATE_LEVEL: &str = "debug";
40
41#[derive(Debug, Deserialize, Serialize)]
42#[serde(rename_all = "lowercase")]
43pub enum LogLevel {
44 Normal,
45 Detailed,
46}
47
48#[derive(Debug, Deserialize, Serialize)]
49#[serde(untagged)]
50pub enum LogStructure {
51 Preset(LogLevel),
52 Explicit(Vec<String>),
53}
54
55fn default_json() -> bool {
56 false
57}
58
59#[derive(Debug, Deserialize, Serialize)]
60pub struct LoggingConfig {
61 pub level: LogStructure,
62
63 #[serde(default = "default_json")]
64 pub json: bool,
65}
66
67impl Default for LoggingConfig {
68 fn default() -> Self {
69 Self {
70 level: LogStructure::Preset(LogLevel::Normal),
71 json: false,
72 }
73 }
74}
75
76impl LoggingConfig {
77 fn get_filter(&self) -> EnvFilter {
78 let directives = match &self.level {
79 LogStructure::Explicit(dirs) => dirs.clone(),
80 &LogStructure::Preset(LogLevel::Normal) => DEFAULT_TRACKED_CRATES
81 .iter()
82 .map(|crate_name| format!("{}={}", crate_name, NORMAL_CRATE_LEVEL))
83 .collect::<Vec<_>>(),
84 LogStructure::Preset(LogLevel::Detailed) => DEFAULT_TRACKED_CRATES
85 .iter()
86 .map(|crate_name| format!("{}={}", crate_name, DETAILED_CRATE_LEVEL))
87 .collect::<Vec<_>>(),
88 };
89
90 let joined_directives = directives.join(",");
91
92 EnvFilter::new(joined_directives)
93 }
94}
95
96fn get_logging_config(path: &Option<PathBuf>) -> Result<LoggingConfig> {
97 let mut builder = Config::builder();
98 builder = builder
99 .set_default("level", "normal")?
100 .set_default("json", false)?
101 .add_source(config::Environment::with_prefix("MENMOS_LOG"));
102
103 if let Some(path) = path {
104 builder = builder.add_source(config::File::from(path.as_ref()))
105 }
106
107 let config: LoggingConfig = builder.build()?.try_deserialize()?;
108
109 Ok(config)
110}
111
112pub fn init_logger(log_cfg_path: &Option<PathBuf>) -> Result<()> {
113 let cfg = get_logging_config(log_cfg_path)?;
114
115 let env_filter = cfg.get_filter();
116
117 if cfg.json {
118 FmtSubscriber::builder()
119 .with_env_filter(env_filter)
120 .json()
121 .finish()
122 .init();
123 } else {
124 FmtSubscriber::builder()
125 .with_env_filter(env_filter)
126 .finish()
127 .init();
128 }
129
130 Ok(())
131}