clamber_core/tracing_logs/
mod.rs1use crate::error::{ClamberError, Result};
2use std::fs;
3use tracing::metadata::LevelFilter;
4use tracing_appender::non_blocking::WorkerGuard;
5use tracing_appender::rolling;
6use tracing_subscriber::filter::filter_fn;
7use tracing_subscriber::fmt::time::ChronoUtc;
8use tracing_subscriber::layer::SubscriberExt;
9use tracing_subscriber::util::SubscriberInitExt;
10use tracing_subscriber::{Layer, fmt};
11
12#[derive(Debug, Clone)]
14pub struct LogConfig {
15 pub time_format: String,
17 pub enable_ansi: bool,
19 pub show_target: bool,
21 pub show_thread_ids: bool,
23 pub compact_format: bool,
25 pub console_level: LevelFilter,
27 pub file_level: LevelFilter,
29}
30
31impl Default for LogConfig {
32 fn default() -> Self {
33 Self {
34 time_format: "%Y-%m-%d %H:%M:%S".to_string(),
35 enable_ansi: true,
36 show_target: false,
37 show_thread_ids: false,
38 compact_format: true,
39 console_level: LevelFilter::INFO,
40 file_level: LevelFilter::INFO,
41 }
42 }
43}
44
45impl LogConfig {
46 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn time_format(mut self, format: impl Into<String>) -> Self {
53 self.time_format = format.into();
54 self
55 }
56
57 pub fn ansi(mut self, enable: bool) -> Self {
59 self.enable_ansi = enable;
60 self
61 }
62
63 pub fn target(mut self, show: bool) -> Self {
65 self.show_target = show;
66 self
67 }
68
69 pub fn thread_ids(mut self, show: bool) -> Self {
71 self.show_thread_ids = show;
72 self
73 }
74
75 pub fn compact(mut self, enable: bool) -> Self {
77 self.compact_format = enable;
78 self
79 }
80
81 pub fn console_level(mut self, level: LevelFilter) -> Self {
83 self.console_level = level;
84 self
85 }
86
87 pub fn file_level(mut self, level: LevelFilter) -> Self {
89 self.file_level = level;
90 self
91 }
92}
93
94pub fn logger_start_with_config(
96 service_name: &str,
97 path: Option<String>,
98 config: LogConfig,
99) -> Result<(WorkerGuard, WorkerGuard)> {
100 let log_dir = match path {
101 Some(p) => format!("logs/{}", p),
102 None => "logs".to_string(),
103 };
104
105 fs::create_dir_all(&log_dir).map_err(|_| ClamberError::DirectoryCreationError {
106 path: log_dir.clone(),
107 })?;
108
109 let info_file = rolling::daily(&log_dir, format!("{}-info.log", service_name));
110 let error_file = rolling::daily(&log_dir, format!("{}-error.log", service_name));
111
112 let (info_writer, info_guard) = tracing_appender::non_blocking(info_file);
113 let (error_writer, error_guard) = tracing_appender::non_blocking(error_file);
114
115 let timer = ChronoUtc::new(config.time_format.clone());
117
118 if config.compact_format {
120 let info_layer = fmt::layer()
122 .compact()
123 .with_writer(info_writer)
124 .with_ansi(false)
125 .with_level(true)
126 .with_target(config.show_target)
127 .with_thread_ids(config.show_thread_ids)
128 .with_timer(timer.clone())
129 .with_filter(filter_fn(move |metadata| {
130 metadata.level() == &tracing::Level::INFO
131 }));
132
133 let error_layer = fmt::layer()
134 .compact()
135 .with_writer(error_writer)
136 .with_ansi(false)
137 .with_level(true)
138 .with_target(config.show_target)
139 .with_thread_ids(config.show_thread_ids)
140 .with_timer(timer.clone())
141 .with_filter(LevelFilter::ERROR);
142
143 let console_layer = fmt::layer()
144 .compact()
145 .with_ansi(config.enable_ansi)
146 .with_level(true)
147 .with_target(config.show_target)
148 .with_thread_ids(config.show_thread_ids)
149 .with_timer(timer)
150 .with_filter(config.console_level);
151
152 tracing_subscriber::registry()
153 .with(info_layer)
154 .with(error_layer)
155 .with(console_layer)
156 .init();
157 } else {
158 let info_layer = fmt::layer()
160 .with_writer(info_writer)
161 .with_ansi(false)
162 .with_level(true)
163 .with_target(config.show_target)
164 .with_thread_ids(config.show_thread_ids)
165 .with_timer(timer.clone())
166 .with_filter(filter_fn(move |metadata| {
167 metadata.level() == &tracing::Level::INFO
168 }));
169
170 let error_layer = fmt::layer()
171 .with_writer(error_writer)
172 .with_ansi(false)
173 .with_level(true)
174 .with_target(config.show_target)
175 .with_thread_ids(config.show_thread_ids)
176 .with_timer(timer.clone())
177 .with_filter(LevelFilter::ERROR);
178
179 let console_layer = fmt::layer()
180 .with_ansi(config.enable_ansi)
181 .with_level(true)
182 .with_target(config.show_target)
183 .with_thread_ids(config.show_thread_ids)
184 .with_timer(timer)
185 .with_filter(config.console_level);
186
187 tracing_subscriber::registry()
188 .with(info_layer)
189 .with(error_layer)
190 .with(console_layer)
191 .init();
192 }
193
194 Ok((info_guard, error_guard))
195}