1use std::collections::HashMap;
2use std::fmt;
3use std::ops::DerefMut;
4use std::path::PathBuf;
5
6use console::Style;
7use log::Record;
8use tracing::{Event, Level};
9use tracing_subscriber::filter::LevelFilter;
10use tracing_subscriber::layer::Context;
11use tracing_subscriber::Layer;
12
13use crate::{LevelOutput, LoggingSubscriberBuilder, LoggingSubscriberLayer, LoggingWriter, LOGGING_WRITER};
14
15#[derive(Default)]
16struct ToStringVisitor<'a>(HashMap<&'a str, String>);
17
18impl fmt::Display for ToStringVisitor<'_> {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 self.0.iter().try_for_each(|(_k, v)| -> fmt::Result { write!(f, "{}", v) })
21 }
22}
23
24impl<'a> tracing::field::Visit for ToStringVisitor<'a> {
25 fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
26 self.0.insert(field.name(), format_args!("{}", value).to_string());
27 }
28
29 fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
30 self.0.insert(field.name(), format_args!("{}", value).to_string());
31 }
32
33 fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
34 self.0.insert(field.name(), format_args!("{}", value).to_string());
35 }
36
37 fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
38 self.0.insert(field.name(), format_args!("{}", value).to_string());
39 }
40
41 fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
42 self.0.insert(field.name(), format_args!("{}", value).to_string());
43 }
44
45 fn record_error(&mut self, field: &tracing::field::Field, value: &(dyn std::error::Error + 'static)) {
46 self.0.insert(field.name(), format_args!("{}", value).to_string());
47 }
48
49 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
50 self.0.insert(field.name(), format_args!("{:?}", value).to_string());
51 }
52}
53
54impl Default for LoggingSubscriberBuilder {
55 fn default() -> Self {
56 LoggingSubscriberBuilder {
57 display_line_number: false,
58 display_level: true,
59 display_time: true,
60 display_target: false,
61 display_filename: false,
62 default_style: Style::new().white(),
63 date_time_style: Style::default().dim(),
64 level_style_error: Style::new().red().bold(),
65 level_style_warn: Style::new().magenta().bold().bright(),
66 level_style_debug: Style::new().blue().bold(),
67 level_style_trace: Style::new().black().bold(),
68 level_style_info: Style::new().green().bright().bold(),
69 style_error: None,
70 style_warn: None,
71 style_info: None,
72 style_debug: None,
73 style_trace: None,
74 min_level: LevelFilter::DEBUG,
75 separator: " ".to_string(),
76 timestamp_format: "%H:%M:%S%.3f".to_string(),
77 format_level: LevelOutput::Long,
78 }
79 }
80}
81
82impl From<LoggingSubscriberBuilder> for LoggingWriter {
83 fn from(value: LoggingSubscriberBuilder) -> Self {
84 let mut logging = LoggingWriter::default();
85 logging.enabled = true;
86 logging.level = value.min_level;
87 logging.default_style = value.default_style;
88 logging.style_error = value.style_error;
89 logging.style_warn = value.style_warn;
90 logging.style_debug = value.style_debug;
91 logging.style_trace = value.style_trace;
92 logging.style_info = value.style_info;
93 logging.level_style_error = value.level_style_error;
94 logging.level_style_warn = value.level_style_warn;
95 logging.level_style_debug = value.level_style_debug;
96 logging.level_style_trace = value.level_style_trace;
97 logging.level_style_info = value.level_style_info;
98 logging.separator = value.separator;
99 logging.timestamp_format = value.timestamp_format;
100 logging.format_level = value.format_level;
101 logging.display_line_number = value.display_line_number;
102 logging.display_level = value.display_level;
103 logging.display_target = value.display_target;
104 logging.display_filename = value.display_filename;
105 logging.display_time = value.display_time;
106 logging.date_time_style = value.date_time_style;
107 logging
108 }
109}
110
111#[allow(dead_code)]
112impl LoggingSubscriberBuilder {
113 pub fn build(self) -> LoggingSubscriberLayer {
114 if let Ok(mut item) = LOGGING_WRITER.lock() {
115 *item = self.into();
116 }
117
118 let subscriber = LoggingSubscriberLayer {};
119 subscriber
120 }
121
122 pub fn with_min_level(mut self, value: LevelFilter) -> Self {
123 self.min_level = value;
124 self
125 }
126
127 pub fn with_separator(mut self, value: String) -> Self {
128 self.separator = value;
129 self
130 }
131 pub fn with_timestamp_format(mut self, value: String) -> Self {
132 self.timestamp_format = value;
133 self
134 }
135 pub fn with_format_level(mut self, value: LevelOutput) -> Self {
136 self.format_level = value;
137 self
138 }
139
140 pub fn with_default_style<S>(mut self, value: S) -> Self
141 where
142 S: Into<Style>,
143 {
144 self.default_style = value.into();
145 self
146 }
147
148 pub fn with_date_time_style<S>(mut self, value: S) -> Self
149 where
150 S: Into<Style>,
151 {
152 self.date_time_style = value.into();
153 self
154 }
155
156 pub fn with_level_style_error<S>(mut self, value: S) -> Self
157 where
158 S: Into<Style>,
159 {
160 self.level_style_error = value.into();
161 self
162 }
163 pub fn with_level_style_warn<S>(mut self, value: S) -> Self
164 where
165 S: Into<Style>,
166 {
167 self.level_style_warn = value.into();
168 self
169 }
170 pub fn with_level_style_debug<S>(mut self, value: S) -> Self
171 where
172 S: Into<Style>,
173 {
174 self.level_style_debug = value.into();
175 self
176 }
177 pub fn with_level_style_trace<S>(mut self, value: S) -> Self
178 where
179 S: Into<Style>,
180 {
181 self.level_style_trace = value.into();
182 self
183 }
184 pub fn with_level_style_info<S>(mut self, value: S) -> Self
185 where
186 S: Into<Style>,
187 {
188 self.level_style_info = value.into();
189 self
190 }
191 pub fn with_style_error<S>(mut self, value: Option<S>) -> Self
192 where
193 S: Into<Style>,
194 {
195 self.style_error = match value {
196 Some(value) => Some(value.into()),
197 None => None,
198 };
199 self
200 }
201 pub fn with_style_warn<S>(mut self, value: Option<S>) -> Self
202 where
203 S: Into<Style>,
204 {
205 self.style_warn = match value {
206 Some(value) => Some(value.into()),
207 None => None,
208 };
209 self
210 }
211 pub fn with_style_info<S>(mut self, value: Option<S>) -> Self
212 where
213 S: Into<Style>,
214 {
215 self.style_info = match value {
216 Some(value) => Some(value.into()),
217 None => None,
218 };
219 self
220 }
221 pub fn with_style_debug<S>(mut self, value: Option<S>) -> Self
222 where
223 S: Into<Style>,
224 {
225 self.style_debug = match value {
226 Some(value) => Some(value.into()),
227 None => None,
228 };
229 self
230 }
231 pub fn with_style_trace<S>(mut self, value: Option<S>) -> Self
232 where
233 S: Into<Style>,
234 {
235 self.style_trace = match value {
236 Some(value) => Some(value.into()),
237 None => None,
238 };
239 self
240 }
241
242 pub fn with_line_number(mut self, display_line_number: bool) -> Self {
243 self.display_line_number = display_line_number;
244 self
245 }
246
247 pub fn with_level(mut self, display_level: bool) -> Self {
248 self.display_level = display_level;
249 self
250 }
251
252 pub fn with_time(mut self, display_time: bool) -> Self {
253 self.display_time = display_time;
254 self
255 }
256
257 pub fn with_target(mut self, display_target: bool) -> Self {
258 self.display_target = display_target;
259 self
260 }
261
262 pub fn with_file(mut self, display_filename: bool) -> Self {
263 self.display_filename = display_filename;
264 self
265 }
266}
267
268impl<S> Layer<S> for LoggingSubscriberLayer
269where
270 S: tracing::Subscriber,
271{
272 fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
273 let mut visitor = ToStringVisitor::default();
274 event.record(&mut visitor);
275
276 let level = match *event.metadata().level() {
277 Level::ERROR => log::Level::Error,
278 Level::WARN => log::Level::Warn,
279 Level::INFO => log::Level::Info,
280 Level::DEBUG => log::Level::Debug,
281 Level::TRACE => log::Level::Trace,
282 };
283
284 let buf = match event.metadata().file() {
285 None => PathBuf::new(),
286 Some(file) => PathBuf::from(file),
287 };
288
289 let filename = buf.file_name().map(|s| s.to_str().unwrap_or("?"));
290
291 let _ = LOGGING_WRITER.lock().unwrap().deref_mut().log(
292 &Record::builder()
293 .args(format_args!("{}", visitor))
294 .level(level.into())
295 .target(event.metadata().target())
296 .file(filename)
297 .line(event.metadata().line())
298 .module_path(event.metadata().module_path())
299 .build(),
300 );
301 }
302}