1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::path::PathBuf;
use std::{fs, path::Path};

use anyhow::Result;

use serde::{Deserialize, Serialize};

use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, FmtSubscriber};

const DEFAULT_TRACKED_CRATES: &[&str] = &[
    "menmosd",
    "amphora",
    "xecute",
    "rapidquery",
    "antidns",
    "lfan",
    "apikit",
    "menmos-client",
    "betterstreams",
    "repository",
    "menmos-std",
    "warp::filters::trace",
];

#[cfg(debug_assertions)]
const NORMAL_CRATE_LEVEL: &str = "debug";

#[cfg(not(debug_assertions))]
const NORMAL_CRATE_LEVEL: &str = "info";

#[cfg(debug_assertions)]
const DETAILED_CRATE_LEVEL: &str = "trace";

#[cfg(not(debug_assertions))]
const DETAILED_CRATE_LEVEL: &str = "debug";

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
    Normal,
    Detailed,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum LogStructure {
    Preset(LogLevel),
    Explicit(Vec<String>),
}

fn default_json() -> bool {
    false
}

#[derive(Debug, Deserialize, Serialize)]
pub struct LoggingConfig {
    pub level: LogStructure,

    #[serde(default = "default_json")]
    pub json: bool,
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self {
            level: LogStructure::Preset(LogLevel::Normal),
            json: false,
        }
    }
}

impl LoggingConfig {
    fn get_filter(&self) -> EnvFilter {
        let directives = match &self.level {
            LogStructure::Explicit(dirs) => dirs.clone(),
            &LogStructure::Preset(LogLevel::Normal) => DEFAULT_TRACKED_CRATES
                .iter()
                .map(|crate_name| format!("{}={}", crate_name, NORMAL_CRATE_LEVEL))
                .collect::<Vec<_>>(),
            LogStructure::Preset(LogLevel::Detailed) => DEFAULT_TRACKED_CRATES
                .iter()
                .map(|crate_name| format!("{}={}", crate_name, DETAILED_CRATE_LEVEL))
                .collect::<Vec<_>>(),
        };

        let joined_directives = directives.join(",");

        EnvFilter::new(joined_directives)
    }
}

fn load_log_config_file(path: &Path) -> Result<LoggingConfig> {
    let f = fs::File::open(path)?;
    let cfg: LoggingConfig = serde_json::from_reader(f)?;
    Ok(cfg)
}

fn get_logging_config(path: &Option<PathBuf>) -> LoggingConfig {
    path.as_ref()
        .map(|p| load_log_config_file(p).ok())
        .flatten()
        .unwrap_or_default()
}

pub fn init_logger(log_cfg_path: &Option<PathBuf>) -> Result<()> {
    let cfg = get_logging_config(log_cfg_path);

    let env_filter = cfg.get_filter();

    if cfg.json {
        FmtSubscriber::builder()
            .with_env_filter(env_filter)
            .json()
            .finish()
            .init();
    } else {
        FmtSubscriber::builder()
            .with_env_filter(env_filter)
            .finish()
            .init();
    }

    Ok(())
}