tracing_human_layer/
layer.rs

1use std::fmt::Debug;
2use std::io::Stdout;
3use std::io::Write;
4use std::sync::atomic::AtomicBool;
5use std::sync::atomic::Ordering;
6
7use parking_lot::Mutex;
8use tracing::span;
9use tracing::Event;
10use tracing::Id;
11use tracing::Level;
12use tracing::Subscriber;
13use tracing_subscriber::fmt::format::FmtSpan;
14use tracing_subscriber::fmt::FormattedFields;
15use tracing_subscriber::layer::Context;
16use tracing_subscriber::registry::LookupSpan;
17use tracing_subscriber::registry::Scope;
18use tracing_subscriber::Layer;
19
20use crate::HumanEvent;
21use crate::HumanFields;
22use crate::Style;
23use crate::StyledSpanFields;
24
25/// A human-friendly [`tracing_subscriber::Layer`].
26pub struct HumanLayer<W = Stdout> {
27    /// We print blank lines before and after long log messages to help visually separate them.
28    ///
29    /// This becomes an issue if two long log messages are printed one after another.
30    ///
31    /// If this variable is `true`, we skip the blank line before to prevent printing two blank
32    /// lines in a row.
33    ///
34    /// This variable is mutated whenever a [`HumanEvent`] is displayed.
35    last_event_was_long: AtomicBool,
36    /// Which span events to emit.
37    span_events: FmtSpan,
38    /// Whether to color the output.
39    color_output: bool,
40    /// The writer where output is written.
41    output_writer: Mutex<W>,
42}
43
44impl<W> Debug for HumanLayer<W> {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.debug_struct("HumanLayer")
47            .field("color_output", &self.color_output)
48            .finish_non_exhaustive()
49    }
50}
51
52impl Default for HumanLayer {
53    fn default() -> Self {
54        Self {
55            last_event_was_long: Default::default(),
56            span_events: FmtSpan::NONE,
57            color_output: true,
58            output_writer: Mutex::new(std::io::stdout()),
59        }
60    }
61}
62
63impl HumanLayer {
64    /// Construct a new [`HumanLayer`] that writes to [`Stdout`].
65    pub fn new() -> Self {
66        Self::default()
67    }
68}
69
70impl<W> HumanLayer<W> {
71    /// Set the writer that log messages are written to.
72    ///
73    /// This does not change colored output by default.
74    pub fn with_output_writer<W2>(self, output_writer: W2) -> HumanLayer<W2> {
75        HumanLayer {
76            last_event_was_long: self.last_event_was_long,
77            span_events: self.span_events,
78            color_output: self.color_output,
79            output_writer: Mutex::new(output_writer),
80        }
81    }
82
83    /// Set the output coloring.
84    pub fn with_color_output(mut self, color_output: bool) -> Self {
85        self.color_output = color_output;
86        self
87    }
88
89    /// Set which span events are logged.
90    pub fn with_span_events(mut self, span_events: FmtSpan) -> Self {
91        self.span_events = span_events;
92        self
93    }
94
95    fn update_long(&self, last_event_was_long: AtomicBool) {
96        self.last_event_was_long
97            .store(last_event_was_long.load(Ordering::SeqCst), Ordering::SeqCst);
98    }
99
100    fn event<S>(&self, level: Level, scope: Option<Scope<'_, S>>) -> HumanEvent
101    where
102        S: tracing::Subscriber,
103        S: for<'lookup> LookupSpan<'lookup>,
104    {
105        HumanEvent::new(
106            level,
107            self.last_event_was_long.load(Ordering::SeqCst).into(),
108            scope,
109            self.color_output,
110        )
111    }
112
113    fn event_for_id<S>(&self, id: &Id, ctx: Context<'_, S>) -> HumanEvent
114    where
115        S: tracing::Subscriber,
116        S: for<'lookup> LookupSpan<'lookup>,
117    {
118        self.event(
119            *ctx.metadata(id)
120                .expect("Metadata should exist for the span ID")
121                .level(),
122            ctx.span_scope(id),
123        )
124    }
125}
126
127impl<S, W> Layer<S> for HumanLayer<W>
128where
129    S: Subscriber,
130    S: for<'lookup> LookupSpan<'lookup>,
131    Self: 'static,
132    W: Write,
133{
134    fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
135        let mut fields = HumanFields::new_span();
136        attrs.record(&mut fields);
137        if let Some(span_ref) = ctx.span(id) {
138            span_ref
139                .extensions_mut()
140                .insert(FormattedFields::<HumanLayer>::new(
141                    StyledSpanFields {
142                        style: Style::new(*attrs.metadata().level(), self.color_output),
143                        fields,
144                    }
145                    .to_string(),
146                ));
147
148            if self.span_events.clone() & FmtSpan::NEW != FmtSpan::NONE {
149                let mut human_event = self.event(*span_ref.metadata().level(), ctx.span_scope(id));
150                human_event.fields.message = Some("new".into());
151                let _ = write!(self.output_writer.lock(), "{human_event}");
152                self.update_long(human_event.last_event_was_long);
153            }
154        }
155    }
156
157    fn on_record(&self, id: &Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
158        let mut fields = HumanFields::new_span();
159        values.record(&mut fields);
160        if let Some(span_ref) = ctx.span(id) {
161            span_ref
162                .extensions_mut()
163                .insert(FormattedFields::<HumanLayer>::new(
164                    StyledSpanFields {
165                        style: Style::new(*span_ref.metadata().level(), self.color_output),
166                        fields,
167                    }
168                    .to_string(),
169                ));
170        }
171    }
172
173    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
174        let mut human_event = self.event(*event.metadata().level(), ctx.event_scope(event));
175        event.record(&mut human_event);
176        let _ = write!(self.output_writer.lock(), "{human_event}");
177        self.update_long(human_event.last_event_was_long);
178    }
179
180    fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
181        if self.span_events.clone() & FmtSpan::ENTER != FmtSpan::NONE {
182            let mut human_event = self.event_for_id(id, ctx);
183            human_event.fields.message = Some("enter".into());
184            let _ = write!(self.output_writer.lock(), "{human_event}");
185            self.update_long(human_event.last_event_was_long);
186        }
187    }
188
189    fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
190        if self.span_events.clone() & FmtSpan::EXIT != FmtSpan::NONE {
191            let mut human_event = self.event_for_id(id, ctx);
192            human_event.fields.message = Some("exit".into());
193            let _ = write!(self.output_writer.lock(), "{human_event}");
194            self.update_long(human_event.last_event_was_long);
195        }
196    }
197
198    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
199        if self.span_events.clone() & FmtSpan::CLOSE != FmtSpan::NONE {
200            let mut human_event = self.event_for_id(&id, ctx);
201            human_event.fields.message = Some("close".into());
202            let _ = write!(self.output_writer.lock(), "{human_event}");
203            self.update_long(human_event.last_event_was_long);
204        }
205    }
206}