1use std::io;
9use tracing_subscriber::{
10 fmt::{self, format::FmtSpan},
11 layer::SubscriberExt,
12 util::SubscriberInitExt,
13 EnvFilter,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum LoggingMode {
19 Cli,
21 Api,
23 Debug,
25 Silent,
27}
28
29#[derive(Debug, Clone)]
31pub struct LoggingConfig {
32 pub mode: LoggingMode,
33 pub level: String,
34 pub show_target: bool,
35 pub show_thread_ids: bool,
36 pub show_file_line: bool,
37 pub use_ansi_colors: bool,
38 pub log_to_file: Option<String>,
39}
40
41impl Default for LoggingConfig {
42 fn default() -> Self {
43 Self {
44 mode: LoggingMode::Api,
45 level: "warn".to_string(),
46 show_target: false,
47 show_thread_ids: false,
48 show_file_line: false,
49 use_ansi_colors: true,
50 log_to_file: None,
51 }
52 }
53}
54
55impl LoggingConfig {
56 pub fn cli(verbose: bool) -> Self {
58 Self {
59 mode: LoggingMode::Cli,
60 level: if verbose {
61 "info".to_string()
62 } else {
63 "warn".to_string()
64 },
65 show_target: verbose,
66 show_thread_ids: false,
67 show_file_line: verbose,
68 use_ansi_colors: true,
69 log_to_file: None,
70 }
71 }
72
73 pub fn api() -> Self {
75 Self {
76 mode: LoggingMode::Api,
77 level: "warn".to_string(),
78 show_target: false,
79 show_thread_ids: false,
80 show_file_line: false,
81 use_ansi_colors: false,
82 log_to_file: None,
83 }
84 }
85
86 pub fn debug() -> Self {
88 Self {
89 mode: LoggingMode::Debug,
90 level: "debug".to_string(),
91 show_target: true,
92 show_thread_ids: true,
93 show_file_line: true,
94 use_ansi_colors: true,
95 log_to_file: Some("turbo-cdn-debug.log".to_string()),
96 }
97 }
98
99 pub fn silent() -> Self {
101 Self {
102 mode: LoggingMode::Silent,
103 level: "error".to_string(),
104 show_target: false,
105 show_thread_ids: false,
106 show_file_line: false,
107 use_ansi_colors: false,
108 log_to_file: None,
109 }
110 }
111}
112
113pub fn init_logging(config: LoggingConfig) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
115 let env_filter = EnvFilter::try_from_default_env()
117 .unwrap_or_else(|_| EnvFilter::new(format!("turbo_cdn={}", config.level)));
118
119 let registry = tracing_subscriber::registry().with(env_filter);
120
121 match config.mode {
122 LoggingMode::Cli => {
123 let fmt_layer = fmt::layer()
125 .with_target(config.show_target)
126 .with_thread_ids(config.show_thread_ids)
127 .with_file(config.show_file_line)
128 .with_line_number(config.show_file_line)
129 .with_ansi(config.use_ansi_colors)
130 .with_span_events(FmtSpan::NONE)
131 .compact();
132
133 registry.with(fmt_layer).init();
134 }
135 LoggingMode::Api => {
136 let fmt_layer = fmt::layer()
138 .with_target(false)
139 .with_thread_ids(false)
140 .with_file(false)
141 .with_line_number(false)
142 .with_ansi(false)
143 .with_span_events(FmtSpan::NONE)
144 .compact()
145 .with_writer(io::stderr);
146
147 registry.with(fmt_layer).init();
148 }
149 LoggingMode::Debug => {
150 let fmt_layer = fmt::layer()
152 .with_target(true)
153 .with_thread_ids(true)
154 .with_file(true)
155 .with_line_number(true)
156 .with_ansi(config.use_ansi_colors)
157 .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE)
158 .pretty();
159
160 if let Some(log_file) = config.log_to_file {
161 let file_appender = tracing_appender::rolling::daily("./logs", log_file);
162 let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
163
164 let file_layer = fmt::layer()
165 .with_target(true)
166 .with_thread_ids(true)
167 .with_file(true)
168 .with_line_number(true)
169 .with_ansi(false)
170 .with_writer(non_blocking)
171 .json();
172
173 registry.with(fmt_layer).with(file_layer).init();
174 } else {
175 registry.with(fmt_layer).init();
176 }
177 }
178 LoggingMode::Silent => {
179 let fmt_layer = fmt::layer()
181 .with_target(false)
182 .with_thread_ids(false)
183 .with_file(false)
184 .with_line_number(false)
185 .with_ansi(false)
186 .with_span_events(FmtSpan::NONE)
187 .compact()
188 .with_writer(io::stderr);
189
190 registry.with(fmt_layer).init();
191 }
192 }
193
194 Ok(())
195}
196
197pub fn init_cli_logging(verbose: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
199 init_logging(LoggingConfig::cli(verbose))
200}
201
202pub fn init_api_logging() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
204 init_logging(LoggingConfig::api())
205}
206
207pub fn init_debug_logging() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
209 init_logging(LoggingConfig::debug())
210}
211
212pub fn init_silent_logging() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
214 init_logging(LoggingConfig::silent())
215}
216
217#[macro_export]
219macro_rules! cli_info {
220 ($($arg:tt)*) => {
221 if tracing::enabled!(tracing::Level::INFO) {
222 tracing::info!($($arg)*);
223 }
224 };
225}
226
227#[macro_export]
228macro_rules! cli_warn {
229 ($($arg:tt)*) => {
230 tracing::warn!($($arg)*);
231 };
232}
233
234#[macro_export]
235macro_rules! cli_error {
236 ($($arg:tt)*) => {
237 tracing::error!($($arg)*);
238 };
239}
240
241#[macro_export]
242macro_rules! api_debug {
243 ($($arg:tt)*) => {
244 if tracing::enabled!(tracing::Level::DEBUG) {
245 tracing::debug!($($arg)*);
246 }
247 };
248}
249
250#[macro_export]
251macro_rules! api_info {
252 ($($arg:tt)*) => {
253 if tracing::enabled!(tracing::Level::INFO) {
254 tracing::info!($($arg)*);
255 }
256 };
257}
258
259pub fn is_verbose() -> bool {
261 tracing::enabled!(tracing::Level::INFO)
262}
263
264pub fn is_debug() -> bool {
266 tracing::enabled!(tracing::Level::DEBUG)
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_logging_config_creation() {
275 let cli_config = LoggingConfig::cli(true);
276 assert_eq!(cli_config.mode, LoggingMode::Cli);
277 assert_eq!(cli_config.level, "info");
278 assert!(cli_config.show_target);
279
280 let api_config = LoggingConfig::api();
281 assert_eq!(api_config.mode, LoggingMode::Api);
282 assert_eq!(api_config.level, "warn");
283 assert!(!api_config.show_target);
284 }
285
286 #[test]
287 fn test_debug_config() {
288 let debug_config = LoggingConfig::debug();
289 assert_eq!(debug_config.mode, LoggingMode::Debug);
290 assert_eq!(debug_config.level, "debug");
291 assert!(debug_config.show_target);
292 assert!(debug_config.show_thread_ids);
293 assert!(debug_config.show_file_line);
294 }
295}