]>
Commit | Line | Data |
---|---|---|
5869c6ff XL |
1 | use super::*; |
2 | use crate::{ | |
3 | field::{VisitFmt, VisitOutput}, | |
4 | fmt::fmt_layer::{FmtContext, FormattedFields}, | |
5 | registry::LookupSpan, | |
6 | }; | |
7 | ||
136023e0 | 8 | use std::fmt::{self, Write}; |
5869c6ff XL |
9 | use tracing_core::{ |
10 | field::{self, Field}, | |
11 | Event, Level, Subscriber, | |
12 | }; | |
13 | ||
14 | #[cfg(feature = "tracing-log")] | |
15 | use tracing_log::NormalizeEvent; | |
16 | ||
17 | use ansi_term::{Colour, Style}; | |
18 | ||
19 | /// An excessively pretty, human-readable event formatter. | |
20 | #[derive(Debug, Clone, Eq, PartialEq)] | |
21 | pub struct Pretty { | |
22 | display_location: bool, | |
23 | } | |
24 | ||
25 | /// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation. | |
26 | /// | |
27 | /// [visitor]: ../../field/trait.Visit.html | |
28 | /// [`DefaultFields`]: struct.DefaultFields.html | |
29 | /// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html | |
30 | pub struct PrettyVisitor<'a> { | |
31 | writer: &'a mut dyn Write, | |
32 | is_empty: bool, | |
cdc7bbd5 | 33 | ansi: bool, |
5869c6ff XL |
34 | style: Style, |
35 | result: fmt::Result, | |
36 | } | |
37 | ||
cdc7bbd5 XL |
38 | /// An excessively pretty, human-readable [`MakeVisitor`] implementation. |
39 | /// | |
40 | /// [`MakeVisitor`]: crate::field::MakeVisitor | |
41 | #[derive(Debug)] | |
42 | pub struct PrettyFields { | |
43 | ansi: bool, | |
44 | } | |
45 | ||
5869c6ff XL |
46 | // === impl Pretty === |
47 | ||
48 | impl Default for Pretty { | |
49 | fn default() -> Self { | |
50 | Self { | |
51 | display_location: true, | |
52 | } | |
53 | } | |
54 | } | |
55 | ||
56 | impl Pretty { | |
57 | fn style_for(level: &Level) -> Style { | |
58 | match *level { | |
59 | Level::TRACE => Style::new().fg(Colour::Purple), | |
60 | Level::DEBUG => Style::new().fg(Colour::Blue), | |
61 | Level::INFO => Style::new().fg(Colour::Green), | |
62 | Level::WARN => Style::new().fg(Colour::Yellow), | |
63 | Level::ERROR => Style::new().fg(Colour::Red), | |
64 | } | |
65 | } | |
66 | ||
67 | /// Sets whether the event's source code location is displayed. | |
68 | /// | |
69 | /// This defaults to `true`. | |
70 | pub fn with_source_location(self, display_location: bool) -> Self { | |
71 | Self { | |
72 | display_location, | |
73 | ..self | |
74 | } | |
75 | } | |
76 | } | |
77 | ||
78 | impl<T> Format<Pretty, T> { | |
79 | /// Sets whether or not the source code location from which an event | |
80 | /// originated is displayed. | |
81 | /// | |
82 | /// This defaults to `true`. | |
83 | pub fn with_source_location(mut self, display_location: bool) -> Self { | |
84 | self.format = self.format.with_source_location(display_location); | |
85 | self | |
86 | } | |
87 | } | |
88 | ||
89 | impl<C, N, T> FormatEvent<C, N> for Format<Pretty, T> | |
90 | where | |
91 | C: Subscriber + for<'a> LookupSpan<'a>, | |
92 | N: for<'a> FormatFields<'a> + 'static, | |
93 | T: FormatTime, | |
94 | { | |
95 | fn format_event( | |
96 | &self, | |
97 | ctx: &FmtContext<'_, C, N>, | |
98 | writer: &mut dyn fmt::Write, | |
99 | event: &Event<'_>, | |
100 | ) -> fmt::Result { | |
101 | #[cfg(feature = "tracing-log")] | |
102 | let normalized_meta = event.normalized_metadata(); | |
103 | #[cfg(feature = "tracing-log")] | |
104 | let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); | |
105 | #[cfg(not(feature = "tracing-log"))] | |
106 | let meta = event.metadata(); | |
107 | write!(writer, " ")?; | |
17df50a5 XL |
108 | |
109 | self.format_timestamp(writer)?; | |
5869c6ff | 110 | |
cdc7bbd5 | 111 | let style = if self.display_level && self.ansi { |
5869c6ff XL |
112 | Pretty::style_for(meta.level()) |
113 | } else { | |
114 | Style::new() | |
115 | }; | |
116 | ||
cdc7bbd5 | 117 | if self.display_level { |
c295e0f8 | 118 | write!(writer, "{} ", super::FmtLevel::new(meta.level(), self.ansi))?; |
cdc7bbd5 XL |
119 | } |
120 | ||
5869c6ff | 121 | if self.display_target { |
cdc7bbd5 | 122 | let target_style = if self.ansi { style.bold() } else { style }; |
5869c6ff XL |
123 | write!( |
124 | writer, | |
125 | "{}{}{}: ", | |
cdc7bbd5 | 126 | target_style.prefix(), |
5869c6ff | 127 | meta.target(), |
cdc7bbd5 | 128 | target_style.infix(style) |
5869c6ff XL |
129 | )?; |
130 | } | |
cdc7bbd5 XL |
131 | let mut v = PrettyVisitor::new(writer, true) |
132 | .with_style(style) | |
133 | .with_ansi(self.ansi); | |
5869c6ff XL |
134 | event.record(&mut v); |
135 | v.finish()?; | |
136 | writer.write_char('\n')?; | |
137 | ||
cdc7bbd5 XL |
138 | let dimmed = if self.ansi { |
139 | Style::new().dimmed().italic() | |
140 | } else { | |
141 | Style::new() | |
142 | }; | |
5869c6ff XL |
143 | let thread = self.display_thread_name || self.display_thread_id; |
144 | if let (true, Some(file), Some(line)) = | |
145 | (self.format.display_location, meta.file(), meta.line()) | |
146 | { | |
147 | write!( | |
148 | writer, | |
149 | " {} {}:{}{}", | |
150 | dimmed.paint("at"), | |
151 | file, | |
152 | line, | |
153 | dimmed.paint(if thread { " " } else { "\n" }) | |
154 | )?; | |
155 | } else if thread { | |
156 | write!(writer, " ")?; | |
157 | } | |
158 | ||
159 | if thread { | |
160 | write!(writer, "{} ", dimmed.paint("on"))?; | |
161 | let thread = std::thread::current(); | |
162 | if self.display_thread_name { | |
163 | if let Some(name) = thread.name() { | |
164 | write!(writer, "{}", name)?; | |
165 | if self.display_thread_id { | |
166 | write!(writer, " ({:?})", thread.id())?; | |
167 | } | |
168 | } else if !self.display_thread_id { | |
169 | write!(writer, " {:?}", thread.id())?; | |
170 | } | |
171 | } else if self.display_thread_id { | |
172 | write!(writer, " {:?}", thread.id())?; | |
173 | } | |
174 | writer.write_char('\n')?; | |
175 | } | |
176 | ||
cdc7bbd5 XL |
177 | let bold = if self.ansi { |
178 | Style::new().bold() | |
179 | } else { | |
180 | Style::new() | |
181 | }; | |
5869c6ff XL |
182 | let span = event |
183 | .parent() | |
c295e0f8 | 184 | .and_then(|id| ctx.span(id)) |
5869c6ff XL |
185 | .or_else(|| ctx.lookup_current()); |
186 | ||
136023e0 | 187 | let scope = span.into_iter().flat_map(|span| span.scope()); |
5869c6ff XL |
188 | |
189 | for span in scope { | |
190 | let meta = span.metadata(); | |
191 | if self.display_target { | |
192 | write!( | |
193 | writer, | |
194 | " {} {}::{}", | |
195 | dimmed.paint("in"), | |
196 | meta.target(), | |
197 | bold.paint(meta.name()), | |
198 | )?; | |
199 | } else { | |
200 | write!( | |
201 | writer, | |
202 | " {} {}", | |
203 | dimmed.paint("in"), | |
204 | bold.paint(meta.name()), | |
205 | )?; | |
206 | } | |
207 | ||
208 | let ext = span.extensions(); | |
209 | let fields = &ext | |
210 | .get::<FormattedFields<N>>() | |
211 | .expect("Unable to find FormattedFields in extensions; this is a bug"); | |
212 | if !fields.is_empty() { | |
213 | write!(writer, " {} {}", dimmed.paint("with"), fields)?; | |
214 | } | |
215 | writer.write_char('\n')?; | |
216 | } | |
217 | ||
218 | writer.write_char('\n') | |
219 | } | |
220 | } | |
221 | ||
222 | impl<'writer> FormatFields<'writer> for Pretty { | |
223 | fn format_fields<R: RecordFields>( | |
224 | &self, | |
225 | writer: &'writer mut dyn fmt::Write, | |
226 | fields: R, | |
227 | ) -> fmt::Result { | |
228 | let mut v = PrettyVisitor::new(writer, true); | |
229 | fields.record(&mut v); | |
230 | v.finish() | |
231 | } | |
232 | ||
233 | fn add_fields(&self, current: &'writer mut String, fields: &span::Record<'_>) -> fmt::Result { | |
234 | let empty = current.is_empty(); | |
235 | let mut v = PrettyVisitor::new(current, empty); | |
236 | fields.record(&mut v); | |
237 | v.finish() | |
238 | } | |
239 | } | |
240 | ||
cdc7bbd5 XL |
241 | // === impl PrettyFields === |
242 | ||
243 | impl Default for PrettyFields { | |
244 | fn default() -> Self { | |
245 | Self::new() | |
246 | } | |
247 | } | |
248 | ||
249 | impl PrettyFields { | |
250 | /// Returns a new default [`PrettyFields`] implementation. | |
251 | pub fn new() -> Self { | |
252 | Self { ansi: true } | |
253 | } | |
254 | ||
255 | /// Enable ANSI encoding for formatted fields. | |
256 | pub fn with_ansi(self, ansi: bool) -> Self { | |
257 | Self { ansi, ..self } | |
258 | } | |
259 | } | |
260 | ||
261 | impl<'a> MakeVisitor<&'a mut dyn Write> for PrettyFields { | |
262 | type Visitor = PrettyVisitor<'a>; | |
263 | ||
264 | #[inline] | |
265 | fn make_visitor(&self, target: &'a mut dyn Write) -> Self::Visitor { | |
266 | PrettyVisitor::new(target, true).with_ansi(self.ansi) | |
267 | } | |
268 | } | |
269 | ||
5869c6ff XL |
270 | // === impl PrettyVisitor === |
271 | ||
272 | impl<'a> PrettyVisitor<'a> { | |
273 | /// Returns a new default visitor that formats to the provided `writer`. | |
274 | /// | |
275 | /// # Arguments | |
276 | /// - `writer`: the writer to format to. | |
277 | /// - `is_empty`: whether or not any fields have been previously written to | |
278 | /// that writer. | |
279 | pub fn new(writer: &'a mut dyn Write, is_empty: bool) -> Self { | |
280 | Self { | |
281 | writer, | |
282 | is_empty, | |
cdc7bbd5 | 283 | ansi: true, |
5869c6ff XL |
284 | style: Style::default(), |
285 | result: Ok(()), | |
286 | } | |
287 | } | |
288 | ||
289 | pub(crate) fn with_style(self, style: Style) -> Self { | |
290 | Self { style, ..self } | |
291 | } | |
292 | ||
cdc7bbd5 XL |
293 | pub(crate) fn with_ansi(self, ansi: bool) -> Self { |
294 | Self { ansi, ..self } | |
295 | } | |
296 | ||
297 | fn write_padded(&mut self, value: &impl fmt::Debug) { | |
298 | let padding = if self.is_empty { | |
5869c6ff | 299 | self.is_empty = false; |
cdc7bbd5 XL |
300 | "" |
301 | } else { | |
302 | ", " | |
303 | }; | |
304 | self.result = write!(self.writer, "{}{:?}", padding, value); | |
305 | } | |
306 | ||
307 | fn bold(&self) -> Style { | |
308 | if self.ansi { | |
309 | self.style.bold() | |
5869c6ff | 310 | } else { |
cdc7bbd5 | 311 | Style::new() |
5869c6ff XL |
312 | } |
313 | } | |
314 | } | |
315 | ||
316 | impl<'a> field::Visit for PrettyVisitor<'a> { | |
317 | fn record_str(&mut self, field: &Field, value: &str) { | |
318 | if self.result.is_err() { | |
319 | return; | |
320 | } | |
321 | ||
322 | if field.name() == "message" { | |
323 | self.record_debug(field, &format_args!("{}", value)) | |
324 | } else { | |
325 | self.record_debug(field, &value) | |
326 | } | |
327 | } | |
328 | ||
329 | fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { | |
330 | if let Some(source) = value.source() { | |
cdc7bbd5 | 331 | let bold = self.bold(); |
5869c6ff XL |
332 | self.record_debug( |
333 | field, | |
334 | &format_args!( | |
c295e0f8 | 335 | "{}, {}{}.sources{}: {}", |
5869c6ff XL |
336 | value, |
337 | bold.prefix(), | |
338 | field, | |
339 | bold.infix(self.style), | |
c295e0f8 | 340 | ErrorSourceList(source), |
5869c6ff XL |
341 | ), |
342 | ) | |
343 | } else { | |
344 | self.record_debug(field, &format_args!("{}", value)) | |
345 | } | |
346 | } | |
347 | ||
348 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { | |
349 | if self.result.is_err() { | |
350 | return; | |
351 | } | |
cdc7bbd5 XL |
352 | let bold = self.bold(); |
353 | match field.name() { | |
354 | "message" => self.write_padded(&format_args!("{}{:?}", self.style.prefix(), value,)), | |
5869c6ff XL |
355 | // Skip fields that are actually log metadata that have already been handled |
356 | #[cfg(feature = "tracing-log")] | |
cdc7bbd5 XL |
357 | name if name.starts_with("log.") => self.result = Ok(()), |
358 | name if name.starts_with("r#") => self.write_padded(&format_args!( | |
5869c6ff XL |
359 | "{}{}{}: {:?}", |
360 | bold.prefix(), | |
361 | &name[2..], | |
362 | bold.infix(self.style), | |
363 | value | |
cdc7bbd5 XL |
364 | )), |
365 | name => self.write_padded(&format_args!( | |
5869c6ff XL |
366 | "{}{}{}: {:?}", |
367 | bold.prefix(), | |
368 | name, | |
369 | bold.infix(self.style), | |
370 | value | |
cdc7bbd5 | 371 | )), |
5869c6ff XL |
372 | }; |
373 | } | |
374 | } | |
375 | ||
376 | impl<'a> VisitOutput<fmt::Result> for PrettyVisitor<'a> { | |
377 | fn finish(self) -> fmt::Result { | |
378 | write!(self.writer, "{}", self.style.suffix())?; | |
379 | self.result | |
380 | } | |
381 | } | |
382 | ||
383 | impl<'a> VisitFmt for PrettyVisitor<'a> { | |
384 | fn writer(&mut self) -> &mut dyn fmt::Write { | |
385 | self.writer | |
386 | } | |
387 | } | |
388 | ||
389 | impl<'a> fmt::Debug for PrettyVisitor<'a> { | |
390 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
391 | f.debug_struct("PrettyVisitor") | |
392 | .field("writer", &format_args!("<dyn fmt::Write>")) | |
393 | .field("is_empty", &self.is_empty) | |
394 | .field("result", &self.result) | |
395 | .field("style", &self.style) | |
cdc7bbd5 | 396 | .field("ansi", &self.ansi) |
5869c6ff XL |
397 | .finish() |
398 | } | |
399 | } |