tui_logger/
tracing_subscriber.rs1use super::TUI_LOGGER;
4use log::{self, Log, Record};
5use std::collections::BTreeMap;
6use std::fmt;
7use tracing_subscriber::registry::LookupSpan;
8use tracing_subscriber::Layer;
9
10#[derive(Default)]
11struct ToStringVisitor<'a>(BTreeMap<&'a str, String>);
12
13impl fmt::Display for ToStringVisitor<'_> {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 self.0.iter().try_for_each(|(k, v)| -> fmt::Result {
16 if *k == "message" {
17 write!(f, " {}", v)
18 } else {
19 write!(f, " {}: {}", k, v)
20 }
21 })
22 }
23}
24
25impl<'a> tracing::field::Visit for ToStringVisitor<'a> {
26 fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
27 self.0
28 .insert(field.name(), format_args!("{}", value).to_string());
29 }
30
31 fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
32 self.0
33 .insert(field.name(), format_args!("{}", value).to_string());
34 }
35
36 fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
37 self.0
38 .insert(field.name(), format_args!("{}", value).to_string());
39 }
40
41 fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
42 self.0
43 .insert(field.name(), format_args!("{}", value).to_string());
44 }
45
46 fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
47 self.0
48 .insert(field.name(), format_args!("{}", value).to_string());
49 }
50
51 fn record_error(
52 &mut self,
53 field: &tracing::field::Field,
54 value: &(dyn std::error::Error + 'static),
55 ) {
56 self.0
57 .insert(field.name(), format_args!("{}", value).to_string());
58 }
59
60 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
61 self.0
62 .insert(field.name(), format_args!("{:?}", value).to_string());
63 }
64}
65
66#[allow(clippy::needless_doctest_main)]
67struct SpanAttributes {
91 attributes: String,
92}
93
94pub struct TuiTracingSubscriberLayer;
95
96impl<S> Layer<S> for TuiTracingSubscriberLayer
97where
98 S: tracing::Subscriber + for<'a> LookupSpan<'a>,
99{
100 fn on_new_span(
101 &self,
102 attrs: &tracing::span::Attributes<'_>,
103 id: &tracing::span::Id,
104 ctx: tracing_subscriber::layer::Context<'_, S>,
105 ) {
106 let mut visitor = ToStringVisitor::default();
107 attrs.record(&mut visitor);
108 ctx.span(id)
109 .unwrap()
110 .extensions_mut()
111 .insert(SpanAttributes {
112 attributes: format!("{}", visitor),
113 });
114 }
115
116 fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) {
117 let mut visitor = ToStringVisitor::default();
118 event.record(&mut visitor);
119
120 let span_attributes = ctx
121 .event_span(event)
122 .and_then(|s| {
123 s.extensions()
124 .get::<SpanAttributes>()
125 .map(|a| a.attributes.to_owned())
126 })
127 .unwrap_or_else(String::new);
128
129 let level = match *event.metadata().level() {
130 tracing::Level::ERROR => log::Level::Error,
131 tracing::Level::WARN => log::Level::Warn,
132 tracing::Level::INFO => log::Level::Info,
133 tracing::Level::DEBUG => log::Level::Debug,
134 tracing::Level::TRACE => log::Level::Trace,
135 };
136
137 TUI_LOGGER.log(
138 &Record::builder()
139 .args(format_args!("{}{}", span_attributes, visitor))
140 .level(level)
141 .target(event.metadata().target())
142 .file(event.metadata().file())
143 .line(event.metadata().line())
144 .module_path(event.metadata().module_path())
145 .build(),
146 );
147 }
148}