1use std::fmt::Debug;
2use std::io::LineWriter;
3use std::io::Stderr;
4use std::io::Write;
5use std::sync::atomic::AtomicBool;
6use std::sync::atomic::Ordering;
7
8use parking_lot::Mutex;
9use tracing::span;
10use tracing::Event;
11use tracing::Id;
12use tracing::Metadata;
13use tracing::Subscriber;
14use tracing_subscriber::fmt::format::FmtSpan;
15use tracing_subscriber::fmt::FormattedFields;
16use tracing_subscriber::layer::Context;
17use tracing_subscriber::registry::LookupSpan;
18use tracing_subscriber::registry::Scope;
19use tracing_subscriber::Layer;
20
21use crate::HumanEvent;
22use crate::HumanFields;
23use crate::LayerStyles;
24use crate::ProvideStyle;
25use crate::ShouldColor;
26use crate::SpanInfo;
27use crate::StyledSpanFields;
28use crate::TextWrapOptionsOwned;
29
30#[cfg(doc)]
31use crate::Style;
32
33pub struct HumanLayer<W = LineWriter<Stderr>, S = LayerStyles> {
35 last_event_was_long: AtomicBool,
44 span_events: FmtSpan,
46 color_output: ShouldColor,
48 textwrap_options: Option<TextWrapOptionsOwned>,
50 output_writer: Mutex<W>,
52 styles: S,
54}
55
56impl<W, S> Debug for HumanLayer<W, S> {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("HumanLayer")
59 .field("span_events", &self.span_events)
60 .field("color_output", &self.color_output)
61 .field("textwrap_options", &self.textwrap_options)
62 .field("output_writer", &std::any::type_name::<W>())
65 .field("styles", &std::any::type_name::<S>())
66 .finish_non_exhaustive()
67 }
68}
69
70impl Default for HumanLayer {
71 fn default() -> Self {
72 Self {
73 last_event_was_long: Default::default(),
74 span_events: FmtSpan::NONE,
75 color_output: ShouldColor::Always,
76 output_writer: Mutex::new(LineWriter::new(std::io::stderr())),
77 styles: LayerStyles::new(),
78 textwrap_options: Some(TextWrapOptionsOwned::new()),
79 }
80 }
81}
82
83impl HumanLayer {
84 pub fn new() -> Self {
86 Self::default()
87 }
88}
89
90impl<W, S> HumanLayer<W, S> {
91 pub fn with_output_writer<W2>(self, output_writer: W2) -> HumanLayer<W2, S> {
98 HumanLayer {
99 last_event_was_long: self.last_event_was_long,
100 span_events: self.span_events,
101 color_output: self.color_output,
102 output_writer: Mutex::new(output_writer),
103 styles: self.styles,
104 textwrap_options: self.textwrap_options,
105 }
106 }
107
108 pub fn with_textwrap_options(mut self, textwrap_options: Option<TextWrapOptionsOwned>) -> Self {
112 self.textwrap_options = textwrap_options;
113 self
114 }
115
116 pub fn with_color_output(mut self, color_output: bool) -> Self {
118 self.color_output = if color_output {
120 ShouldColor::Always
121 } else {
122 ShouldColor::Never
123 };
124 self
125 }
126
127 pub fn with_span_events(mut self, span_events: FmtSpan) -> Self {
129 self.span_events = span_events;
130 self
131 }
132
133 pub fn with_style_provider<S2>(self, styles: S2) -> HumanLayer<W, S2> {
136 HumanLayer {
137 last_event_was_long: self.last_event_was_long,
138 span_events: self.span_events,
139 color_output: self.color_output,
140 output_writer: self.output_writer,
141 styles,
142 textwrap_options: self.textwrap_options,
143 }
144 }
145
146 fn update_long(&self, last_event_was_long: AtomicBool) {
147 self.last_event_was_long
148 .store(last_event_was_long.load(Ordering::SeqCst), Ordering::SeqCst);
149 }
150}
151
152impl<W, S> HumanLayer<W, S>
153where
154 S: ProvideStyle,
155{
156 fn event<R>(
157 &self,
158 metadata: &'static Metadata<'static>,
159 scope: Option<Scope<'_, R>>,
160 ) -> HumanEvent<'_>
161 where
162 R: tracing::Subscriber,
163 R: for<'lookup> LookupSpan<'lookup>,
164 {
165 HumanEvent {
166 last_event_was_long: self.last_event_was_long.load(Ordering::SeqCst).into(),
169 style: self.styles.for_metadata(metadata),
170 color: self.color_output,
171 spans: scope
172 .map(|scope| SpanInfo::from_scope(scope))
173 .unwrap_or_default(),
174 fields: HumanFields::new_event(),
175 textwrap_options: self.textwrap_options.as_ref().map(|options| options.into()),
176 }
177 }
178
179 fn event_for_id<U>(&self, id: &Id, ctx: Context<'_, U>) -> HumanEvent<'_>
180 where
181 U: tracing::Subscriber,
182 U: for<'lookup> LookupSpan<'lookup>,
183 {
184 self.event(
185 ctx.metadata(id)
186 .expect("Metadata should exist for the span ID"),
187 ctx.span_scope(id),
188 )
189 }
190}
191
192impl<Sub, Wr, Sty> Layer<Sub> for HumanLayer<Wr, Sty>
193where
194 Sub: Subscriber,
195 Sub: for<'lookup> LookupSpan<'lookup>,
196 Self: 'static,
197 Wr: Write,
198 Sty: ProvideStyle,
199{
200 fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &Id, ctx: Context<'_, Sub>) {
201 let mut fields = HumanFields::new_span();
202 attrs.record(&mut fields);
203 if let Some(span_ref) = ctx.span(id) {
204 span_ref
205 .extensions_mut()
206 .insert(FormattedFields::<HumanLayer>::new(
207 StyledSpanFields {
208 style: self.styles.for_metadata(attrs.metadata()),
209 color: self.color_output,
210 fields,
211 }
212 .to_string(),
213 ));
214
215 if self.span_events.clone() & FmtSpan::NEW != FmtSpan::NONE {
216 let mut human_event = self.event(span_ref.metadata(), ctx.span_scope(id));
217 human_event.fields.message = Some("new".into());
218 let _ = write!(self.output_writer.lock(), "{human_event}");
219 self.update_long(human_event.last_event_was_long);
220 }
221 }
222 }
223
224 fn on_record(&self, id: &Id, values: &span::Record<'_>, ctx: Context<'_, Sub>) {
225 let mut fields = HumanFields::new_span();
226 values.record(&mut fields);
227 if let Some(span_ref) = ctx.span(id) {
228 span_ref
229 .extensions_mut()
230 .insert(FormattedFields::<HumanLayer>::new(
231 StyledSpanFields {
232 style: self.styles.for_metadata(span_ref.metadata()),
233 color: self.color_output,
234 fields,
235 }
236 .to_string(),
237 ));
238 }
239 }
240
241 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, Sub>) {
242 let mut human_event = self.event(event.metadata(), ctx.event_scope(event));
243 event.record(&mut human_event);
244 let _ = write!(self.output_writer.lock(), "{human_event}");
245 self.update_long(human_event.last_event_was_long);
246 }
247
248 fn on_enter(&self, id: &Id, ctx: Context<'_, Sub>) {
249 if self.span_events.clone() & FmtSpan::ENTER != FmtSpan::NONE {
250 let mut human_event = self.event_for_id(id, ctx);
251 human_event.fields.message = Some("enter".into());
252 let _ = write!(self.output_writer.lock(), "{human_event}");
253 self.update_long(human_event.last_event_was_long);
254 }
255 }
256
257 fn on_exit(&self, id: &Id, ctx: Context<'_, Sub>) {
258 if self.span_events.clone() & FmtSpan::EXIT != FmtSpan::NONE {
259 let mut human_event = self.event_for_id(id, ctx);
260 human_event.fields.message = Some("exit".into());
261 let _ = write!(self.output_writer.lock(), "{human_event}");
262 self.update_long(human_event.last_event_was_long);
263 }
264 }
265
266 fn on_close(&self, id: Id, ctx: Context<'_, Sub>) {
267 if self.span_events.clone() & FmtSpan::CLOSE != FmtSpan::NONE {
268 let mut human_event = self.event_for_id(&id, ctx);
269 human_event.fields.message = Some("close".into());
270 let _ = write!(self.output_writer.lock(), "{human_event}");
271 self.update_long(human_event.last_event_was_long);
272 }
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use expect_test::expect;
279
280 use super::*;
281
282 #[test]
283 fn test_debug() {
284 expect![[r#"
285 HumanLayer {
286 span_events: FmtSpan::NONE,
287 color_output: Always,
288 textwrap_options: Some(
289 TextWrapOptionsOwned {
290 width: Fixed(
291 80,
292 ),
293 line_ending: LF,
294 break_words: false,
295 wrap_algorithm: OptimalFit(
296 Penalties {
297 nline_penalty: 1000,
298 overflow_penalty: 2500,
299 short_last_line_fraction: 4,
300 short_last_line_penalty: 25,
301 hyphen_penalty: 25,
302 },
303 ),
304 word_separator: AsciiSpace,
305 word_splitter: NoHyphenation,
306 },
307 ),
308 output_writer: "std::io::buffered::linewriter::LineWriter<std::io::stdio::Stderr>",
309 styles: "tracing_human_layer::style::LayerStyles",
310 ..
311 }"#]]
312 .assert_eq(&format!("{:#?}", HumanLayer::new()));
313 }
314}