agglayer_config/
log.rs

1use std::{fmt::Display, path::PathBuf};
2
3use serde::{Deserialize, Deserializer, Serialize};
4use tracing_subscriber::{fmt::writer::BoxMakeWriter, EnvFilter};
5
6/// The log configuration.
7#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq)]
8#[serde(rename_all = "kebab-case")]
9pub struct Log {
10    /// The `RUST_LOG` environment variable will take precedence over the
11    /// configuration log level.
12    #[serde(default)]
13    pub level: LogLevel,
14    #[serde(default)]
15    pub outputs: Vec<LogOutput>,
16    #[serde(default)]
17    pub format: LogFormat,
18}
19
20/// The log format.
21#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)]
22#[serde(rename_all = "lowercase")]
23pub enum LogFormat {
24    #[default]
25    Pretty,
26    Json,
27}
28
29/// The log level.
30#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)]
31#[serde(rename_all = "lowercase")]
32pub enum LogLevel {
33    Trace,
34    Debug,
35    #[default]
36    Info,
37    Warn,
38    Error,
39    Fatal,
40}
41
42impl Display for LogLevel {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        let level = match self {
45            LogLevel::Trace => "trace",
46            LogLevel::Debug => "debug",
47            LogLevel::Info => "info",
48            LogLevel::Warn => "warn",
49            LogLevel::Error => "error",
50            LogLevel::Fatal => "fatal",
51        };
52
53        write!(f, "{level}")
54    }
55}
56
57impl From<LogLevel> for EnvFilter {
58    fn from(value: LogLevel) -> Self {
59        EnvFilter::new(format!("warn,agglayer={value},pessimistic_proof={value}"))
60    }
61}
62
63/// The log output.
64///
65/// This can be either `stdout`, `stderr`, or a file path.
66///
67/// The [`Deserialize`] implementation allows for the configuration file to
68/// specify the output location as a string, which is then parsed into the
69/// appropriate enum variant. If the string is not recognized to be either
70/// `stdout` or `stderr`, it is assumed to be a file path.
71#[derive(Serialize, Debug, Clone, Default, PartialEq, Eq)]
72pub enum LogOutput {
73    #[default]
74    Stdout,
75    Stderr,
76    File(PathBuf),
77}
78
79impl<'de> Deserialize<'de> for LogOutput {
80    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
81    where
82        D: Deserializer<'de>,
83    {
84        let s = String::deserialize(deserializer)?;
85        // If the string is not recognized to be either `stdout` or `stderr`,
86        // it is assumed to be a file path.
87        match s.as_str() {
88            "stdout" => Ok(LogOutput::Stdout),
89            "stderr" => Ok(LogOutput::Stderr),
90            _ => Ok(LogOutput::File(PathBuf::from(s))),
91        }
92    }
93}
94
95impl LogOutput {
96    /// Get a [`BoxMakeWriter`] for the log output.
97    ///
98    /// This can be used to plug the log output into the tracing subscriber.
99    pub fn as_make_writer(&self) -> BoxMakeWriter {
100        match self {
101            LogOutput::Stdout => BoxMakeWriter::new(std::io::stdout),
102            LogOutput::Stderr => BoxMakeWriter::new(std::io::stderr),
103            LogOutput::File(path) => {
104                let appender = tracing_appender::rolling::never(".", path);
105                BoxMakeWriter::new(appender)
106            }
107        }
108    }
109}