logging_subscriber/
logging_subscriber.rs

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}