clamber_core/tracing_logs/
mod.rs1use 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#[derive(Debug, Clone)]
16pub struct LogConfig {
17 pub time_format: String,
19 pub enable_ansi: bool,
21 pub show_target: bool,
23 pub show_thread_ids: bool,
25 pub compact_format: bool,
27 pub console_level: LevelFilter,
29 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 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn time_format(mut self, format: impl Into<String>) -> Self {
55 self.time_format = format.into();
56 self
57 }
58
59 pub fn ansi(mut self, enable: bool) -> Self {
61 self.enable_ansi = enable;
62 self
63 }
64
65 pub fn target(mut self, show: bool) -> Self {
67 self.show_target = show;
68 self
69 }
70
71 pub fn thread_ids(mut self, show: bool) -> Self {
73 self.show_thread_ids = show;
74 self
75 }
76
77 pub fn compact(mut self, enable: bool) -> Self {
79 self.compact_format = enable;
80 self
81 }
82
83 pub fn console_level(mut self, level: LevelFilter) -> Self {
85 self.console_level = level;
86 self
87 }
88
89 pub fn file_level(mut self, level: LevelFilter) -> Self {
91 self.file_level = level;
92 self
93 }
94}
95
96pub 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 let timer = ChronoUtc::new(config.time_format.clone());
119
120 if config.compact_format {
122 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 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}