clamber_core/tracing_logs/
mod.rs

1//! tracing 日志模块:提供控制台+文件输出、按日滚动、可配置格式/级别的初始化函数。
2//! 参见 README 以及 examples/beautiful_logs_custom.rs。
3use crate::error::{ClamberError, Result};
4use std::fs;
5use tracing::metadata::LevelFilter;
6use tracing_appender::non_blocking::WorkerGuard;
7use tracing_appender::rolling;
8use tracing_subscriber::filter::filter_fn;
9use tracing_subscriber::fmt::time::ChronoUtc;
10use tracing_subscriber::layer::SubscriberExt;
11use tracing_subscriber::util::SubscriberInitExt;
12use tracing_subscriber::{Layer, fmt};
13
14/// 日志配置结构
15#[derive(Debug, Clone)]
16pub struct LogConfig {
17    /// 时间格式字符串
18    pub time_format: String,
19    /// 是否启用ANSI颜色(控制台)
20    pub enable_ansi: bool,
21    /// 是否显示目标模块
22    pub show_target: bool,
23    /// 是否显示线程ID
24    pub show_thread_ids: bool,
25    /// 是否使用紧凑格式
26    pub compact_format: bool,
27    /// 控制台日志级别
28    pub console_level: LevelFilter,
29    /// 文件日志级别
30    pub file_level: LevelFilter,
31}
32
33impl Default for LogConfig {
34    fn default() -> Self {
35        Self {
36            time_format: "%Y-%m-%d %H:%M:%S".to_string(),
37            enable_ansi: true,
38            show_target: false,
39            show_thread_ids: false,
40            compact_format: true,
41            console_level: LevelFilter::INFO,
42            file_level: LevelFilter::INFO,
43        }
44    }
45}
46
47impl LogConfig {
48    /// 创建新的日志配置
49    pub fn new() -> Self {
50        Self::default()
51    }
52
53    /// 设置时间格式
54    pub fn time_format(mut self, format: impl Into<String>) -> Self {
55        self.time_format = format.into();
56        self
57    }
58
59    /// 启用/禁用ANSI颜色
60    pub fn ansi(mut self, enable: bool) -> Self {
61        self.enable_ansi = enable;
62        self
63    }
64
65    /// 显示/隐藏目标模块
66    pub fn target(mut self, show: bool) -> Self {
67        self.show_target = show;
68        self
69    }
70
71    /// 显示/隐藏线程ID
72    pub fn thread_ids(mut self, show: bool) -> Self {
73        self.show_thread_ids = show;
74        self
75    }
76
77    /// 设置紧凑格式
78    pub fn compact(mut self, enable: bool) -> Self {
79        self.compact_format = enable;
80        self
81    }
82
83    /// 设置控制台日志级别
84    pub fn console_level(mut self, level: LevelFilter) -> Self {
85        self.console_level = level;
86        self
87    }
88
89    /// 设置文件日志级别
90    pub fn file_level(mut self, level: LevelFilter) -> Self {
91        self.file_level = level;
92        self
93    }
94}
95
96/// 使用自定义配置初始化日志系统
97pub fn logger_start_with_config(
98    service_name: &str,
99    path: Option<String>,
100    config: LogConfig,
101) -> Result<(WorkerGuard, WorkerGuard)> {
102    let log_dir = match path {
103        Some(p) => format!("logs/{}", p),
104        None => "logs".to_string(),
105    };
106
107    fs::create_dir_all(&log_dir).map_err(|_| ClamberError::DirectoryCreationError {
108        path: log_dir.clone(),
109    })?;
110
111    let info_file = rolling::daily(&log_dir, format!("{}-info.log", service_name));
112    let error_file = rolling::daily(&log_dir, format!("{}-error.log", service_name));
113
114    let (info_writer, info_guard) = tracing_appender::non_blocking(info_file);
115    let (error_writer, error_guard) = tracing_appender::non_blocking(error_file);
116
117    // 使用用户配置的时间格式
118    let timer = ChronoUtc::new(config.time_format.clone());
119
120    // 根据配置选择格式类型
121    if config.compact_format {
122        // 使用紧凑格式
123        let info_layer = fmt::layer()
124            .compact()
125            .with_writer(info_writer)
126            .with_ansi(false)
127            .with_level(true)
128            .with_target(config.show_target)
129            .with_thread_ids(config.show_thread_ids)
130            .with_timer(timer.clone())
131            .with_filter(filter_fn(move |metadata| {
132                metadata.level() == &tracing::Level::INFO
133            }));
134
135        let error_layer = fmt::layer()
136            .compact()
137            .with_writer(error_writer)
138            .with_ansi(false)
139            .with_level(true)
140            .with_target(config.show_target)
141            .with_thread_ids(config.show_thread_ids)
142            .with_timer(timer.clone())
143            .with_filter(LevelFilter::ERROR);
144
145        let console_layer = fmt::layer()
146            .compact()
147            .with_ansi(config.enable_ansi)
148            .with_level(true)
149            .with_target(config.show_target)
150            .with_thread_ids(config.show_thread_ids)
151            .with_timer(timer)
152            .with_filter(config.console_level);
153
154        tracing_subscriber::registry()
155            .with(info_layer)
156            .with(error_layer)
157            .with(console_layer)
158            .init();
159    } else {
160        // 使用完整格式
161        let info_layer = fmt::layer()
162            .with_writer(info_writer)
163            .with_ansi(false)
164            .with_level(true)
165            .with_target(config.show_target)
166            .with_thread_ids(config.show_thread_ids)
167            .with_timer(timer.clone())
168            .with_filter(filter_fn(move |metadata| {
169                metadata.level() == &tracing::Level::INFO
170            }));
171
172        let error_layer = fmt::layer()
173            .with_writer(error_writer)
174            .with_ansi(false)
175            .with_level(true)
176            .with_target(config.show_target)
177            .with_thread_ids(config.show_thread_ids)
178            .with_timer(timer.clone())
179            .with_filter(LevelFilter::ERROR);
180
181        let console_layer = fmt::layer()
182            .with_ansi(config.enable_ansi)
183            .with_level(true)
184            .with_target(config.show_target)
185            .with_thread_ids(config.show_thread_ids)
186            .with_timer(timer)
187            .with_filter(config.console_level);
188
189        tracing_subscriber::registry()
190            .with(info_layer)
191            .with(error_layer)
192            .with(console_layer)
193            .init();
194    }
195
196    Ok((info_guard, error_guard))
197}