1use tracing::Level;
7use tracing_subscriber::{
8 EnvFilter,
9 fmt::{self, format::FmtSpan},
10 layer::SubscriberExt,
11 util::SubscriberInitExt,
12};
13
14#[derive(Debug, Clone)]
16pub struct LoggingConfig {
17 pub level: LogLevel,
19 pub with_timestamps: bool,
21 pub with_thread_ids: bool,
23 pub with_source_location: bool,
25 pub with_span_events: bool,
27 pub json_format: bool,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum LogLevel {
34 Trace,
36 Debug,
38 Info,
40 Warn,
42 Error,
44}
45
46impl LogLevel {
47 fn to_tracing_level(&self) -> Level {
49 match self {
50 LogLevel::Trace => Level::TRACE,
51 LogLevel::Debug => Level::DEBUG,
52 LogLevel::Info => Level::INFO,
53 LogLevel::Warn => Level::WARN,
54 LogLevel::Error => Level::ERROR,
55 }
56 }
57}
58
59impl Default for LoggingConfig {
60 fn default() -> Self {
61 Self {
62 level: LogLevel::Info,
63 with_timestamps: true,
64 with_thread_ids: false,
65 with_source_location: false,
66 with_span_events: false,
67 json_format: false,
68 }
69 }
70}
71
72impl LoggingConfig {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_level(mut self, level: LogLevel) -> Self {
80 self.level = level;
81 self
82 }
83
84 pub fn with_timestamps(mut self, enable: bool) -> Self {
86 self.with_timestamps = enable;
87 self
88 }
89
90 pub fn with_thread_ids(mut self, enable: bool) -> Self {
92 self.with_thread_ids = enable;
93 self
94 }
95
96 pub fn with_source_location(mut self, enable: bool) -> Self {
98 self.with_source_location = enable;
99 self
100 }
101
102 pub fn with_span_events(mut self, enable: bool) -> Self {
104 self.with_span_events = enable;
105 self
106 }
107
108 pub fn with_json_format(mut self, enable: bool) -> Self {
110 self.json_format = enable;
111 self
112 }
113
114 pub fn development() -> Self {
116 Self {
117 level: LogLevel::Debug,
118 with_timestamps: true,
119 with_thread_ids: true,
120 with_source_location: true,
121 with_span_events: true,
122 json_format: false,
123 }
124 }
125
126 pub fn production() -> Self {
128 Self {
129 level: LogLevel::Info,
130 with_timestamps: true,
131 with_thread_ids: false,
132 with_source_location: false,
133 with_span_events: false,
134 json_format: true, }
136 }
137}
138
139pub fn init_logging(config: LoggingConfig) {
151 let env_filter = EnvFilter::try_from_default_env()
152 .or_else(|_| EnvFilter::try_new(config.level.to_tracing_level().as_str()))
153 .unwrap_or_else(|_| EnvFilter::new("info"));
154
155 let span_events = if config.with_span_events {
156 FmtSpan::ENTER | FmtSpan::CLOSE
157 } else {
158 FmtSpan::NONE
159 };
160
161 if config.json_format {
162 let fmt_layer = fmt::layer()
164 .json()
165 .with_span_events(span_events)
166 .with_current_span(true)
167 .with_thread_ids(config.with_thread_ids)
168 .with_file(config.with_source_location)
169 .with_line_number(config.with_source_location);
170
171 tracing_subscriber::registry()
172 .with(env_filter)
173 .with(fmt_layer)
174 .init();
175 } else {
176 let fmt_layer = fmt::layer()
178 .with_span_events(span_events)
179 .with_thread_ids(config.with_thread_ids)
180 .with_file(config.with_source_location)
181 .with_line_number(config.with_source_location)
182 .with_target(config.with_source_location);
183
184 tracing_subscriber::registry()
185 .with(env_filter)
186 .with(fmt_layer)
187 .init();
188 }
189}
190
191pub fn init_default_logging() {
195 init_logging(LoggingConfig::default());
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_logging_config_default() {
204 let config = LoggingConfig::default();
205 assert_eq!(config.level, LogLevel::Info);
206 assert!(config.with_timestamps);
207 assert!(!config.with_thread_ids);
208 }
209
210 #[test]
211 fn test_logging_config_development() {
212 let config = LoggingConfig::development();
213 assert_eq!(config.level, LogLevel::Debug);
214 assert!(config.with_span_events);
215 assert!(!config.json_format);
216 }
217
218 #[test]
219 fn test_logging_config_production() {
220 let config = LoggingConfig::production();
221 assert_eq!(config.level, LogLevel::Info);
222 assert!(config.json_format);
223 assert!(!config.with_source_location);
224 }
225
226 #[test]
227 fn test_logging_config_builder() {
228 let config = LoggingConfig::new()
229 .with_level(LogLevel::Trace)
230 .with_timestamps(false)
231 .with_thread_ids(true)
232 .with_json_format(true);
233
234 assert_eq!(config.level, LogLevel::Trace);
235 assert!(!config.with_timestamps);
236 assert!(config.with_thread_ids);
237 assert!(config.json_format);
238 }
239}