clamber_core/tracing_logs/
mod.rs

1use 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/// 日志配置结构
13#[derive(Debug, Clone)]
14pub struct LogConfig {
15    /// 时间格式字符串
16    pub time_format: String,
17    /// 是否启用ANSI颜色(控制台)
18    pub enable_ansi: bool,
19    /// 是否显示目标模块
20    pub show_target: bool,
21    /// 是否显示线程ID
22    pub show_thread_ids: bool,
23    /// 是否使用紧凑格式
24    pub compact_format: bool,
25    /// 控制台日志级别
26    pub console_level: LevelFilter,
27    /// 文件日志级别
28    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    /// 创建新的日志配置
47    pub fn new() -> Self {
48        Self::default()
49    }
50
51    /// 设置时间格式
52    pub fn time_format(mut self, format: impl Into<String>) -> Self {
53        self.time_format = format.into();
54        self
55    }
56
57    /// 启用/禁用ANSI颜色
58    pub fn ansi(mut self, enable: bool) -> Self {
59        self.enable_ansi = enable;
60        self
61    }
62
63    /// 显示/隐藏目标模块
64    pub fn target(mut self, show: bool) -> Self {
65        self.show_target = show;
66        self
67    }
68
69    /// 显示/隐藏线程ID
70    pub fn thread_ids(mut self, show: bool) -> Self {
71        self.show_thread_ids = show;
72        self
73    }
74
75    /// 设置紧凑格式
76    pub fn compact(mut self, enable: bool) -> Self {
77        self.compact_format = enable;
78        self
79    }
80
81    /// 设置控制台日志级别
82    pub fn console_level(mut self, level: LevelFilter) -> Self {
83        self.console_level = level;
84        self
85    }
86
87    /// 设置文件日志级别
88    pub fn file_level(mut self, level: LevelFilter) -> Self {
89        self.file_level = level;
90        self
91    }
92}
93
94// pub fn logger_start(
95//     service_name: &str,
96//     path: Option<String>,
97// ) -> Result<(WorkerGuard, WorkerGuard)> {
98//     let log_dir = match path {
99//         Some(p) => format!("{}/logs", p),
100//         None => format!("logs"),
101//     };
102
103//     fs::create_dir_all(&log_dir).map_err(|_| ClamberError::DirectoryCreationError {
104//         path: log_dir.clone(),
105//     })?;
106
107//     let info_file = rolling::daily(&log_dir, format!("{}-info.log", service_name));
108//     let error_file = rolling::daily(&log_dir, format!("{}-error.log", service_name));
109
110//     let (info_writer, info_guard) = tracing_appender::non_blocking(info_file);
111//     let (error_writer, error_guard) = tracing_appender::non_blocking(error_file);
112
113//     // 创建自定义时间格式:yyyy-MM-dd HH:mm:ss
114//     let timer = ChronoUtc::new("%Y-%m-%d %H:%M:%S".to_string());
115
116//     let info_layer = fmt::layer()
117//         .with_writer(info_writer)
118//         .with_ansi(false)
119//         .with_level(true)
120//         .with_target(true)
121//         .with_thread_ids(false)
122//         .with_timer(timer.clone())
123//         .with_filter(filter_fn(|metadata| {
124//             metadata.level() == &tracing::Level::INFO
125//         }));
126
127//     let error_layer = fmt::layer()
128//         .with_writer(error_writer)
129//         .with_ansi(false)
130//         .with_level(true)
131//         .with_target(true)
132//         .with_thread_ids(false)
133//         .with_timer(timer.clone())
134//         .with_filter(LevelFilter::ERROR);
135
136//     let console_layer = fmt::layer()
137//         .with_ansi(true)
138//         .with_level(true)
139//         .with_target(false) // 控制台不显示模块路径以保持简洁
140//         .with_thread_ids(false)
141//         .with_timer(timer)
142//         .compact() // 使用紧凑格式
143//         .with_filter(LevelFilter::INFO);
144
145//     tracing_subscriber::registry()
146//         .with(info_layer)
147//         .with(error_layer)
148//         .with(console_layer)
149//         .init();
150
151//     Ok((info_guard, error_guard))
152// }
153
154/// 使用自定义配置初始化日志系统
155pub fn logger_start_with_config(
156    service_name: &str,
157    path: Option<String>,
158    config: LogConfig,
159) -> Result<(WorkerGuard, WorkerGuard)> {
160    let log_dir = match path {
161        Some(p) => format!("logs/{}", p),
162        None => format!("logs"),
163    };
164
165    fs::create_dir_all(&log_dir).map_err(|_| ClamberError::DirectoryCreationError {
166        path: log_dir.clone(),
167    })?;
168
169    let info_file = rolling::daily(&log_dir, format!("{}-info.log", service_name));
170    let error_file = rolling::daily(&log_dir, format!("{}-error.log", service_name));
171
172    let (info_writer, info_guard) = tracing_appender::non_blocking(info_file);
173    let (error_writer, error_guard) = tracing_appender::non_blocking(error_file);
174
175    // 使用用户配置的时间格式
176    let timer = ChronoUtc::new(config.time_format.clone());
177
178    // 根据配置选择格式类型
179    if config.compact_format {
180        // 使用紧凑格式
181        let info_layer = fmt::layer()
182            .compact()
183            .with_writer(info_writer)
184            .with_ansi(false)
185            .with_level(true)
186            .with_target(config.show_target)
187            .with_thread_ids(config.show_thread_ids)
188            .with_timer(timer.clone())
189            .with_filter(filter_fn(move |metadata| {
190                metadata.level() == &tracing::Level::INFO
191            }));
192
193        let error_layer = fmt::layer()
194            .compact()
195            .with_writer(error_writer)
196            .with_ansi(false)
197            .with_level(true)
198            .with_target(config.show_target)
199            .with_thread_ids(config.show_thread_ids)
200            .with_timer(timer.clone())
201            .with_filter(LevelFilter::ERROR);
202
203        let console_layer = fmt::layer()
204            .compact()
205            .with_ansi(config.enable_ansi)
206            .with_level(true)
207            .with_target(config.show_target)
208            .with_thread_ids(config.show_thread_ids)
209            .with_timer(timer)
210            .with_filter(config.console_level);
211
212        tracing_subscriber::registry()
213            .with(info_layer)
214            .with(error_layer)
215            .with(console_layer)
216            .init();
217    } else {
218        // 使用完整格式
219        let info_layer = fmt::layer()
220            .with_writer(info_writer)
221            .with_ansi(false)
222            .with_level(true)
223            .with_target(config.show_target)
224            .with_thread_ids(config.show_thread_ids)
225            .with_timer(timer.clone())
226            .with_filter(filter_fn(move |metadata| {
227                metadata.level() == &tracing::Level::INFO
228            }));
229
230        let error_layer = fmt::layer()
231            .with_writer(error_writer)
232            .with_ansi(false)
233            .with_level(true)
234            .with_target(config.show_target)
235            .with_thread_ids(config.show_thread_ids)
236            .with_timer(timer.clone())
237            .with_filter(LevelFilter::ERROR);
238
239        let console_layer = fmt::layer()
240            .with_ansi(config.enable_ansi)
241            .with_level(true)
242            .with_target(config.show_target)
243            .with_thread_ids(config.show_thread_ids)
244            .with_timer(timer)
245            .with_filter(config.console_level);
246
247        tracing_subscriber::registry()
248            .with(info_layer)
249            .with(error_layer)
250            .with(console_layer)
251            .init();
252    }
253
254    Ok((info_guard, error_guard))
255}