objectstore_log/config.rs
1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use tracing::level_filters::LevelFilter;
5
6/// Log output format.
7///
8/// Controls how log messages are formatted. The format can be explicitly specified or
9/// auto-detected based on whether output is to a TTY.
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
11#[serde(rename_all = "lowercase")]
12pub enum LogFormat {
13 /// Auto detect the best format.
14 ///
15 /// This chooses [`LogFormat::Pretty`] for TTY, otherwise [`LogFormat::Simplified`].
16 Auto,
17
18 /// Pretty printing with colors.
19 ///
20 /// ```text
21 /// INFO objectstore::http > objectstore starting
22 /// ```
23 Pretty,
24
25 /// Simplified plain text output.
26 ///
27 /// ```text
28 /// 2020-12-04T12:10:32Z [objectstore::http] INFO: objectstore starting
29 /// ```
30 Simplified,
31
32 /// Dump out JSON lines.
33 ///
34 /// ```text
35 /// {"timestamp":"2020-12-04T12:11:08.729716Z","level":"INFO","logger":"objectstore::http","message":"objectstore starting","module_path":"objectstore::http","filename":"objectstore_service/src/http.rs","lineno":31}
36 /// ```
37 Json,
38}
39
40/// The logging format parse error.
41#[derive(Clone, Debug)]
42pub struct FormatParseError(String);
43
44impl fmt::Display for FormatParseError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(
47 f,
48 r#"error parsing "{}" as format: expected one of "auto", "pretty", "simplified", "json""#,
49 self.0
50 )
51 }
52}
53
54impl std::str::FromStr for LogFormat {
55 type Err = FormatParseError;
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 let result = match s {
59 "" => LogFormat::Auto,
60 s if s.eq_ignore_ascii_case("auto") => LogFormat::Auto,
61 s if s.eq_ignore_ascii_case("pretty") => LogFormat::Pretty,
62 s if s.eq_ignore_ascii_case("simplified") => LogFormat::Simplified,
63 s if s.eq_ignore_ascii_case("json") => LogFormat::Json,
64 s => return Err(FormatParseError(s.into())),
65 };
66
67 Ok(result)
68 }
69}
70
71impl std::error::Error for FormatParseError {}
72
73mod display_fromstr {
74 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
75 where
76 S: serde::Serializer,
77 T: std::fmt::Display,
78 {
79 serializer.collect_str(&value)
80 }
81
82 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
83 where
84 D: serde::Deserializer<'de>,
85 T: std::str::FromStr,
86 <T as std::str::FromStr>::Err: std::fmt::Display,
87 {
88 use serde::Deserialize;
89 let s = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
90 s.parse().map_err(serde::de::Error::custom)
91 }
92}
93
94/// Logging configuration.
95///
96/// Controls the verbosity and format of log output. Logs are always written to stderr.
97#[derive(Debug, Deserialize, Serialize)]
98pub struct LoggingConfig {
99 /// Minimum log level to output.
100 ///
101 /// Controls which log messages are emitted based on their severity. Messages at or above this
102 /// level will be output. Valid levels in increasing severity: TRACE, DEBUG, INFO, WARN, ERROR,
103 /// OFF.
104 ///
105 /// The `RUST_LOG` environment variable provides more granular control per module if needed.
106 ///
107 /// **Important**: Levels `DEBUG` and `TRACE` are very verbose and can impact performance; use
108 /// only for debugging.
109 ///
110 /// # Default
111 ///
112 /// `INFO`
113 ///
114 /// # Environment Variable
115 ///
116 /// `OS__LOGGING__LEVEL`
117 ///
118 /// # Considerations
119 ///
120 /// - `TRACE` and `DEBUG` can be very verbose and impact performance; use only for debugging
121 /// - `INFO` is appropriate for production
122 /// - `WARN` or `ERROR` can be used to reduce log volume in high-traffic systems
123 #[serde(with = "display_fromstr")]
124 pub level: LevelFilter,
125
126 /// Log output format.
127 ///
128 /// Determines how log messages are formatted. See [`LogFormat`] for available options and
129 /// examples.
130 ///
131 /// # Default
132 ///
133 /// `Auto` (pretty for TTY, simplified otherwise)
134 ///
135 /// # Environment Variable
136 ///
137 /// `OS__LOGGING__FORMAT`
138 pub format: LogFormat,
139}
140
141impl Default for LoggingConfig {
142 fn default() -> Self {
143 Self {
144 level: LevelFilter::INFO,
145 format: LogFormat::Auto,
146 }
147 }
148}