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/// 使用自定义配置初始化日志系统
95pub 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    // 使用用户配置的时间格式
116    let timer = ChronoUtc::new(config.time_format.clone());
117
118    // 根据配置选择格式类型
119    if config.compact_format {
120        // 使用紧凑格式
121        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        // 使用完整格式
159        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}