daoyi_cloud_common/config/
log_config.rs1use serde::Deserialize;
3use tracing_appender::non_blocking::WorkerGuard;
4use tracing_subscriber::fmt;
5
6use tracing_appender::rolling;
7
8use super::default_true;
9
10const FORMAT_PRETTY: &str = "pretty";
11const FORMAT_COMPACT: &str = "compact";
12const FORMAT_JSON: &str = "json";
13const FORMAT_FULL: &str = "full";
14
15#[derive(Deserialize, Clone, Debug)]
16pub struct LogConfig {
17 #[serde(default = "default_filter_level")]
18 pub filter_level: String,
19 #[serde(default = "default_true")]
20 pub with_ansi: bool,
21 #[serde(default = "default_true")]
22 pub stdout: bool,
23 #[serde(default = "default_directory")]
24 pub directory: String,
25 #[serde(default = "default_file_name")]
26 pub file_name: String,
27 #[serde(default = "default_rolling")]
28 pub rolling: String,
29 #[serde(default = "default_format")]
30 pub format: String,
31 #[serde(default = "default_true")]
32 pub with_level: bool,
33 #[serde(default = "default_true")]
34 pub with_target: bool,
35 #[serde(default = "default_true")]
36 pub with_thread_ids: bool,
37 #[serde(default = "default_true")]
38 pub with_thread_names: bool,
39 #[serde(default = "default_true")]
40 pub with_source_location: bool,
41}
42fn default_filter_level() -> String {
43 "info".into()
44}
45fn default_directory() -> String {
46 "./logs".into()
47}
48fn default_file_name() -> String {
49 "app.log".into()
50}
51fn default_rolling() -> String {
52 "daily".into()
53}
54fn default_format() -> String {
55 FORMAT_FULL.into()
56}
57
58impl Default for LogConfig {
59 fn default() -> Self {
60 Self {
61 filter_level: default_filter_level(),
62 with_ansi: true,
63 stdout: false,
64 directory: default_directory(),
65 file_name: default_file_name(),
66 rolling: default_rolling(),
67 format: default_format(),
68 with_level: true,
69 with_target: true,
70 with_thread_ids: true,
71 with_thread_names: true,
72 with_source_location: true,
73 }
74 }
75}
76
77#[allow(dead_code)]
78impl LogConfig {
79 pub fn filter_level(mut self, filter_level: &str) -> Self {
86 self.filter_level = filter_level.to_owned();
87 self
88 }
89
90 pub fn with_ansi(mut self, with_ansi: bool) -> Self {
92 self.with_ansi = with_ansi;
93 self
94 }
95
96 pub fn stdout(mut self, stdout: bool) -> Self {
98 self.stdout = stdout;
99 self
100 }
101
102 pub fn directory(mut self, directory: impl Into<String>) -> Self {
104 self.directory = directory.into();
105 self
106 }
107
108 pub fn file_name(mut self, file_name: impl Into<String>) -> Self {
110 self.file_name = file_name.into();
111 self
112 }
113
114 pub fn rolling(mut self, rolling: impl Into<String>) -> Self {
118 let rolling = rolling.into();
119 if !["minutely", "hourly", "daily", "never"].contains(&&*rolling) {
120 panic!("Unknown rolling")
121 }
122 self.rolling = rolling;
123 self
124 }
125
126 pub fn format(mut self, format: impl Into<String>) -> Self {
130 let format = format.into();
131 if format != FORMAT_PRETTY
132 && format != FORMAT_COMPACT
133 && format != FORMAT_JSON
134 && format != FORMAT_FULL
135 {
136 panic!("Unknown format")
137 }
138 self.format = format;
139 self
140 }
141
142 pub fn with_level(mut self, with_level: bool) -> Self {
144 self.with_level = with_level;
145 self
146 }
147
148 pub fn with_target(mut self, with_target: bool) -> Self {
150 self.with_target = with_target;
151 self
152 }
153
154 pub fn with_thread_ids(mut self, with_thread_ids: bool) -> Self {
156 self.with_thread_ids = with_thread_ids;
157 self
158 }
159
160 pub fn with_thread_names(mut self, with_thread_names: bool) -> Self {
162 self.with_thread_names = with_thread_names;
163 self
164 }
165
166 pub fn with_source_location(mut self, with_source_location: bool) -> Self {
168 self.with_source_location = with_source_location;
169 self
170 }
171
172 pub fn guard(&self) -> WorkerGuard {
176 let file_appender = match &*self.rolling {
178 "minutely" => rolling::minutely(&self.directory, &self.file_name),
179 "hourly" => rolling::hourly(&self.directory, &self.file_name),
180 "daily" => rolling::daily(&self.directory, &self.file_name),
181 "never" => rolling::never(&self.directory, &self.file_name),
182 _ => rolling::never(&self.directory, &self.file_name),
183 };
184 let (file_writer, guard) = tracing_appender::non_blocking(file_appender);
185
186 let subscriber = tracing_subscriber::fmt()
188 .with_env_filter(
189 tracing_subscriber::EnvFilter::try_from_default_env()
190 .unwrap_or(tracing_subscriber::EnvFilter::new(&self.filter_level)),
191 )
192 .with_ansi(self.with_ansi);
193
194 if self.format == FORMAT_PRETTY {
195 let subscriber = subscriber.event_format(
196 fmt::format()
197 .with_timer(fmt::time::OffsetTime::new(
198 time::OffsetDateTime::now_local().unwrap().offset(),
199 time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]").unwrap(),
200 ))
201 .pretty()
202 .with_level(self.with_level)
203 .with_target(self.with_target)
204 .with_thread_ids(self.with_thread_ids)
205 .with_thread_names(self.with_thread_names)
206 .with_source_location(self.with_source_location),
207 );
208 if self.stdout {
209 subscriber.with_writer(std::io::stdout).init();
210 } else {
211 subscriber.with_writer(file_writer).init();
212 };
213 } else if self.format == FORMAT_COMPACT {
214 let subscriber = subscriber.event_format(
215 fmt::format()
216 .with_timer(fmt::time::OffsetTime::new(
217 time::OffsetDateTime::now_local().unwrap().offset(),
218 time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]").unwrap(),
219 ))
220 .compact()
221 .with_level(self.with_level)
222 .with_target(self.with_target)
223 .with_thread_ids(self.with_thread_ids)
224 .with_thread_names(self.with_thread_names)
225 .with_source_location(self.with_source_location),
226 );
227 if self.stdout {
228 subscriber.with_writer(std::io::stdout).init();
229 } else {
230 subscriber.with_writer(file_writer).init();
231 };
232 } else if self.format == FORMAT_JSON {
233 let subscriber = subscriber.event_format(
234 fmt::format()
235 .with_timer(fmt::time::OffsetTime::new(
236 time::OffsetDateTime::now_local().unwrap().offset(),
237 time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]").unwrap(),
238 ))
239 .json()
240 .with_level(self.with_level)
241 .with_target(self.with_target)
242 .with_thread_ids(self.with_thread_ids)
243 .with_thread_names(self.with_thread_names)
244 .with_source_location(self.with_source_location),
245 );
246 if self.stdout {
247 subscriber.json().with_writer(std::io::stdout).init();
248 } else {
249 subscriber.json().with_writer(file_writer).init();
250 };
251 } else if self.format == FORMAT_FULL {
252 let subscriber = subscriber.event_format(
253 fmt::format()
254 .with_timer(fmt::time::OffsetTime::new(
255 time::OffsetDateTime::now_local().unwrap().offset(),
256 time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]").unwrap(),
257 ))
258 .with_level(self.with_level)
259 .with_target(self.with_target)
260 .with_thread_ids(self.with_thread_ids)
261 .with_thread_names(self.with_thread_names)
262 .with_source_location(self.with_source_location),
263 );
264 if self.stdout {
265 subscriber.with_writer(std::io::stdout).init();
266 } else {
267 subscriber.with_writer(file_writer).init();
268 };
269 }
270
271 guard
273 }
274}