]> git.proxmox.com Git - rustc.git/blame - vendor/tracing-subscriber/src/fmt/format/mod.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / vendor / tracing-subscriber / src / fmt / format / mod.rs
CommitLineData
f035d41b 1//! Formatters for logging `tracing` events.
5e7ed085
FG
2//!
3//! This module provides several formatter implementations, as well as utilities
4//! for implementing custom formatters.
5//!
6//! # Formatters
7//! This module provides a number of formatter implementations:
8//!
9//! * [`Full`]: The default formatter. This emits human-readable,
10//! single-line logs for each event that occurs, with the current span context
11//! displayed before the formatted representation of the event. See
12//! [here](Full#example-output) for sample output.
13//!
14//! * [`Compact`]: A variant of the default formatter, optimized for
15//! short line lengths. Fields from the current span context are appended to
16//! the fields of the formatted event, and span names are not shown; the
17//! verbosity level is abbreviated to a single character. See
18//! [here](Compact#example-output) for sample output.
19//!
20//! * [`Pretty`]: Emits excessively pretty, multi-line logs, optimized
21//! for human readability. This is primarily intended to be used in local
22//! development and debugging, or for command-line applications, where
23//! automated analysis and compact storage of logs is less of a priority than
24//! readability and visual appeal. See [here](Pretty#example-output)
25//! for sample output.
26//!
27//! * [`Json`]: Outputs newline-delimited JSON logs. This is intended
28//! for production use with systems where structured logs are consumed as JSON
29//! by analysis and viewing tools. The JSON output is not optimized for human
30//! readability. See [here](Json#example-output) for sample output.
17df50a5 31use super::time::{FormatTime, SystemTime};
f035d41b
XL
32use crate::{
33 field::{MakeOutput, MakeVisitor, RecordFields, VisitFmt, VisitOutput},
34 fmt::fmt_layer::FmtContext,
35 fmt::fmt_layer::FormattedFields,
36 registry::LookupSpan,
37};
38
a2a8927a 39use std::fmt::{self, Debug, Display, Write};
f035d41b
XL
40use tracing_core::{
41 field::{self, Field, Visit},
42 span, Event, Level, Subscriber,
43};
44
45#[cfg(feature = "tracing-log")]
46use tracing_log::NormalizeEvent;
47
48#[cfg(feature = "ansi")]
49use ansi_term::{Colour, Style};
50
51#[cfg(feature = "json")]
52mod json;
f035d41b
XL
53#[cfg(feature = "json")]
54#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
55pub use json::*;
56
5869c6ff
XL
57#[cfg(feature = "ansi")]
58mod pretty;
59#[cfg(feature = "ansi")]
60#[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
61pub use pretty::*;
62
a2a8927a 63/// A type that can format a tracing [`Event`] to a [`Writer`].
f035d41b 64///
a2a8927a
XL
65/// `FormatEvent` is primarily used in the context of [`fmt::Subscriber`] or
66/// [`fmt::Layer`]. Each time an event is dispatched to [`fmt::Subscriber`] or
67/// [`fmt::Layer`], the subscriber or layer
68/// forwards it to its associated `FormatEvent` to emit a log message.
f035d41b
XL
69///
70/// This trait is already implemented for function pointers with the same
71/// signature as `format_event`.
72///
a2a8927a
XL
73/// # Arguments
74///
75/// The following arguments are passed to `FormatEvent::format_event`:
76///
77/// * A [`FmtContext`]. This is an extension of the [`layer::Context`] type,
78/// which can be used for accessing stored information such as the current
79/// span context an event occurred in.
80///
81/// In addition, [`FmtContext`] exposes access to the [`FormatFields`]
82/// implementation that the subscriber was configured to use via the
83/// [`FmtContext::field_format`] method. This can be used when the
84/// [`FormatEvent`] implementation needs to format the event's fields.
85///
86/// For convenience, [`FmtContext`] also [implements `FormatFields`],
87/// forwarding to the configured [`FormatFields`] type.
88///
89/// * A [`Writer`] to which the formatted representation of the event is
90/// written. This type implements the [`std::fmt::Write`] trait, and therefore
91/// can be used with the [`std::write!`] and [`std::writeln!`] macros, as well
92/// as calling [`std::fmt::Write`] methods directly.
93///
94/// The [`Writer`] type also implements additional methods that provide
95/// information about how the event should be formatted. The
96/// [`Writer::has_ansi_escapes`] method indicates whether [ANSI terminal
97/// escape codes] are supported by the underlying I/O writer that the event
98/// will be written to. If this returns `true`, the formatter is permitted to
99/// use ANSI escape codes to add colors and other text formatting to its
100/// output. If it returns `false`, the event will be written to an output that
101/// does not support ANSI escape codes (such as a log file), and they should
102/// not be emitted.
103///
104/// Crates like [`ansi_term`] and [`owo-colors`] can be used to add ANSI
105/// escape codes to formatted output.
106///
107/// * The actual [`Event`] to be formatted.
108///
6a06907d
XL
109/// # Examples
110///
a2a8927a
XL
111/// This example re-implements a simiplified version of this crate's [default
112/// formatter]:
113///
6a06907d 114/// ```rust
a2a8927a 115/// use std::fmt;
6a06907d 116/// use tracing_core::{Subscriber, Event};
a2a8927a
XL
117/// use tracing_subscriber::fmt::{
118/// format::{self, FormatEvent, FormatFields},
119/// FmtContext,
120/// FormattedFields,
121/// };
6a06907d
XL
122/// use tracing_subscriber::registry::LookupSpan;
123///
124/// struct MyFormatter;
125///
126/// impl<S, N> FormatEvent<S, N> for MyFormatter
127/// where
128/// S: Subscriber + for<'a> LookupSpan<'a>,
129/// N: for<'a> FormatFields<'a> + 'static,
130/// {
131/// fn format_event(
132/// &self,
133/// ctx: &FmtContext<'_, S, N>,
a2a8927a 134/// mut writer: format::Writer<'_>,
6a06907d
XL
135/// event: &Event<'_>,
136/// ) -> fmt::Result {
a2a8927a
XL
137/// // Format values from the event's's metadata:
138/// let metadata = event.metadata();
139/// write!(&mut writer, "{} {}: ", metadata.level(), metadata.target())?;
6a06907d 140///
a2a8927a
XL
141/// // Format all the spans in the event's span context.
142/// if let Some(scope) = ctx.event_scope() {
143/// for span in scope.from_root() {
144/// write!(writer, "{}", span.name())?;
6a06907d 145///
a2a8927a
XL
146/// // `FormattedFields` is a formatted representation of the span's
147/// // fields, which is stored in its extensions by the `fmt` layer's
148/// // `new_span` method. The fields will have been formatted
149/// // by the same field formatter that's provided to the event
150/// // formatter in the `FmtContext`.
151/// let ext = span.extensions();
152/// let fields = &ext
153/// .get::<FormattedFields<N>>()
154/// .expect("will never be `None`");
6a06907d 155///
a2a8927a
XL
156/// // Skip formatting the fields if the span had no fields.
157/// if !fields.is_empty() {
158/// write!(writer, "{{{}}}", fields)?;
159/// }
160/// write!(writer, ": ")?;
6a06907d 161/// }
a2a8927a 162/// }
6a06907d
XL
163///
164/// // Write fields on the event
a2a8927a 165/// ctx.field_format().format_fields(writer.by_ref(), event)?;
6a06907d
XL
166///
167/// writeln!(writer)
168/// }
169/// }
a2a8927a
XL
170///
171/// let _subscriber = tracing_subscriber::fmt()
172/// .event_format(MyFormatter)
173/// .init();
174///
175/// let _span = tracing::info_span!("my_span", answer = 42).entered();
176/// tracing::info!(question = "life, the universe, and everything", "hello world");
6a06907d
XL
177/// ```
178///
179/// This formatter will print events like this:
180///
181/// ```text
182/// DEBUG yak_shaving::shaver: some-span{field-on-span=foo}: started shaving yak
183/// ```
184///
5099ac24 185/// [`layer::Context`]: crate::layer::Context
a2a8927a
XL
186/// [`fmt::Layer`]: super::Layer
187/// [`fmt::Subscriber`]: super::Subscriber
188/// [`Event`]: tracing::Event
189/// [implements `FormatFields`]: super::FmtContext#impl-FormatFields<'writer>
190/// [ANSI terminal escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code
191/// [`Writer::has_ansi_escapes`]: Writer::has_ansi_escapes
192/// [`ansi_term`]: https://crates.io/crates/ansi_term
193/// [`owo-colors`]: https://crates.io/crates/owo-colors
194/// [default formatter]: Full
f035d41b
XL
195pub trait FormatEvent<S, N>
196where
197 S: Subscriber + for<'a> LookupSpan<'a>,
198 N: for<'a> FormatFields<'a> + 'static,
199{
a2a8927a 200 /// Write a log message for `Event` in `Context` to the given [`Writer`].
f035d41b
XL
201 fn format_event(
202 &self,
203 ctx: &FmtContext<'_, S, N>,
a2a8927a 204 writer: Writer<'_>,
f035d41b
XL
205 event: &Event<'_>,
206 ) -> fmt::Result;
207}
208
209impl<S, N> FormatEvent<S, N>
a2a8927a 210 for fn(ctx: &FmtContext<'_, S, N>, Writer<'_>, &Event<'_>) -> fmt::Result
f035d41b
XL
211where
212 S: Subscriber + for<'a> LookupSpan<'a>,
213 N: for<'a> FormatFields<'a> + 'static,
214{
215 fn format_event(
216 &self,
217 ctx: &FmtContext<'_, S, N>,
a2a8927a 218 writer: Writer<'_>,
f035d41b
XL
219 event: &Event<'_>,
220 ) -> fmt::Result {
221 (*self)(ctx, writer, event)
222 }
223}
a2a8927a 224/// A type that can format a [set of fields] to a [`Writer`].
f035d41b
XL
225///
226/// `FormatFields` is primarily used in the context of [`FmtSubscriber`]. Each
227/// time a span or event with fields is recorded, the subscriber will format
228/// those fields with its associated `FormatFields` implementation.
229///
230/// [set of fields]: ../field/trait.RecordFields.html
231/// [`FmtSubscriber`]: ../fmt/struct.Subscriber.html
232pub trait FormatFields<'writer> {
a2a8927a
XL
233 /// Format the provided `fields` to the provided [`Writer`], returning a result.
234 fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result;
f035d41b
XL
235
236 /// Record additional field(s) on an existing span.
237 ///
238 /// By default, this appends a space to the current set of fields if it is
239 /// non-empty, and then calls `self.format_fields`. If different behavior is
240 /// required, the default implementation of this method can be overridden.
a2a8927a
XL
241 fn add_fields(
242 &self,
243 current: &'writer mut FormattedFields<Self>,
244 fields: &span::Record<'_>,
245 ) -> fmt::Result {
246 if !current.fields.is_empty() {
247 current.fields.push(' ');
f035d41b 248 }
a2a8927a 249 self.format_fields(current.as_writer(), fields)
f035d41b
XL
250 }
251}
252
253/// Returns the default configuration for an [event formatter].
254///
255/// Methods on the returned event formatter can be used for further
256/// configuration. For example:
257///
258/// ```rust
259/// let format = tracing_subscriber::fmt::format()
260/// .without_time() // Don't include timestamps
261/// .with_target(false) // Don't include event targets.
262/// .with_level(false) // Don't include event levels.
263/// .compact(); // Use a more compact, abbreviated format.
264///
265/// // Use the configured formatter when building a new subscriber.
266/// tracing_subscriber::fmt()
267/// .event_format(format)
268/// .init();
269/// ```
270pub fn format() -> Format {
271 Format::default()
272}
273
274/// Returns the default configuration for a JSON [event formatter].
275#[cfg(feature = "json")]
276#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
277pub fn json() -> Format<Json> {
278 format().json()
279}
280
281/// Returns a [`FormatFields`] implementation that formats fields using the
282/// provided function or closure.
283///
284/// [`FormatFields`]: trait.FormatFields.html
285pub fn debug_fn<F>(f: F) -> FieldFn<F>
286where
a2a8927a 287 F: Fn(&mut Writer<'_>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone,
f035d41b
XL
288{
289 FieldFn(f)
290}
291
a2a8927a
XL
292/// A writer to which formatted representations of spans and events are written.
293///
294/// This type is provided as input to the [`FormatEvent::format_event`] and
295/// [`FormatFields::format_fields`] methods, which will write formatted
296/// representations of [`Event`]s and [fields] to the `Writer`.
297///
298/// This type implements the [`std::fmt::Write`] trait, allowing it to be used
299/// with any function that takes an instance of [`std::fmt::Write`].
300/// Additionally, it can be used with the standard library's [`std::write!`] and
301/// [`std::writeln!`] macros.
302///
303/// Additionally, a `Writer` may expose additional `tracing`-specific
304/// information to the formatter implementation.
5099ac24
FG
305///
306/// [fields]: tracing_core::field
a2a8927a
XL
307pub struct Writer<'writer> {
308 writer: &'writer mut dyn fmt::Write,
309 // TODO(eliza): add ANSI support
310 is_ansi: bool,
311}
312
f035d41b
XL
313/// A [`FormatFields`] implementation that formats fields by calling a function
314/// or closure.
315///
316/// [`FormatFields`]: trait.FormatFields.html
317#[derive(Debug, Clone)]
318pub struct FieldFn<F>(F);
319/// The [visitor] produced by [`FieldFn`]'s [`MakeVisitor`] implementation.
320///
321/// [visitor]: ../../field/trait.Visit.html
322/// [`FieldFn`]: struct.FieldFn.html
323/// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html
324pub struct FieldFnVisitor<'a, F> {
325 f: F,
a2a8927a 326 writer: Writer<'a>,
f035d41b
XL
327 result: fmt::Result,
328}
5e7ed085 329/// Marker for [`Format`] that indicates that the compact log format should be used.
f035d41b 330///
5099ac24 331/// The compact format includes fields from all currently entered spans, after
5e7ed085
FG
332/// the event's fields. Span names are listed in order before fields are
333/// displayed.
334///
335/// # Example Output
336///
337/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-compact
338/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
339/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-compact`
340/// <font color="#AAAAAA">2022-02-17T19:51:05.809287Z </font><font color="#4E9A06"> INFO</font> <b>fmt_compact</b><font color="#AAAAAA">: preparing to shave yaks </font><i>number_of_yaks</i><font color="#AAAAAA">=3</font>
341/// <font color="#AAAAAA">2022-02-17T19:51:05.809367Z </font><font color="#4E9A06"> INFO</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: shaving yaks </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
342/// <font color="#AAAAAA">2022-02-17T19:51:05.809414Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot; </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=1</font>
343/// <font color="#AAAAAA">2022-02-17T19:51:05.809443Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: yak shaved successfully </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=1</font>
344/// <font color="#AAAAAA">2022-02-17T19:51:05.809477Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks</b>: <b>yak_events</b><font color="#AAAAAA">: </font><i>yak</i><font color="#AAAAAA">=1 </font><i>shaved</i><font color="#AAAAAA">=true </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
345/// <font color="#AAAAAA">2022-02-17T19:51:05.809500Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: </font><i>yaks_shaved</i><font color="#AAAAAA">=1 </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
346/// <font color="#AAAAAA">2022-02-17T19:51:05.809531Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot; </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=2</font>
347/// <font color="#AAAAAA">2022-02-17T19:51:05.809554Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: yak shaved successfully </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=2</font>
348/// <font color="#AAAAAA">2022-02-17T19:51:05.809581Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks</b>: <b>yak_events</b><font color="#AAAAAA">: </font><i>yak</i><font color="#AAAAAA">=2 </font><i>shaved</i><font color="#AAAAAA">=true </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
349/// <font color="#AAAAAA">2022-02-17T19:51:05.809606Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: </font><i>yaks_shaved</i><font color="#AAAAAA">=2 </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
350/// <font color="#AAAAAA">2022-02-17T19:51:05.809635Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot; </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=3</font>
351/// <font color="#AAAAAA">2022-02-17T19:51:05.809664Z </font><font color="#C4A000"> WARN</font> <b>shaving_yaks</b>:<b>shave</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: could not locate yak </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3 </font><font color="#AAAAAA"><i>yak</i></font><font color="#AAAAAA">=3</font>
352/// <font color="#AAAAAA">2022-02-17T19:51:05.809693Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks</b>: <b>yak_events</b><font color="#AAAAAA">: </font><i>yak</i><font color="#AAAAAA">=3 </font><i>shaved</i><font color="#AAAAAA">=false </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
353/// <font color="#AAAAAA">2022-02-17T19:51:05.809717Z </font><font color="#CC0000">ERROR</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: failed to shave yak </font><i>yak</i><font color="#AAAAAA">=3 </font><i>error</i><font color="#AAAAAA">=missing yak </font><i>error.sources</i><font color="#AAAAAA">=[out of space, out of cash] </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
354/// <font color="#AAAAAA">2022-02-17T19:51:05.809743Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks</b>: <b>fmt_compact::yak_shave</b><font color="#AAAAAA">: </font><i>yaks_shaved</i><font color="#AAAAAA">=2 </font><font color="#AAAAAA"><i>yaks</i></font><font color="#AAAAAA">=3</font>
355/// <font color="#AAAAAA">2022-02-17T19:51:05.809768Z </font><font color="#4E9A06"> INFO</font> <b>fmt_compact</b><font color="#AAAAAA">: yak shaving completed </font><i>all_yaks_shaved</i><font color="#AAAAAA">=false</font>
356///
357/// </pre>
f035d41b
XL
358#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
359pub struct Compact;
360
5e7ed085 361/// Marker for [`Format`] that indicates that the default log format should be used.
f035d41b 362///
5e7ed085 363/// This formatter shows the span context before printing event data. Spans are
5099ac24 364/// displayed including their names and fields.
5e7ed085
FG
365///
366/// # Example Output
367///
368/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt
369/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
370/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt`
371/// <font color="#AAAAAA">2022-02-15T18:40:14.289898Z </font><font color="#4E9A06"> INFO</font> fmt: preparing to shave yaks <i>number_of_yaks</i><font color="#AAAAAA">=3</font>
372/// <font color="#AAAAAA">2022-02-15T18:40:14.289974Z </font><font color="#4E9A06"> INFO</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: shaving yaks</font>
373/// <font color="#AAAAAA">2022-02-15T18:40:14.290011Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=1</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot;</font>
374/// <font color="#AAAAAA">2022-02-15T18:40:14.290038Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=1</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: yak shaved successfully</font>
375/// <font color="#AAAAAA">2022-02-15T18:40:14.290070Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: yak_events: </font><i>yak</i><font color="#AAAAAA">=1 </font><i>shaved</i><font color="#AAAAAA">=true</font>
376/// <font color="#AAAAAA">2022-02-15T18:40:14.290089Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: </font><i>yaks_shaved</i><font color="#AAAAAA">=1</font>
377/// <font color="#AAAAAA">2022-02-15T18:40:14.290114Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=2</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot;</font>
378/// <font color="#AAAAAA">2022-02-15T18:40:14.290134Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=2</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: yak shaved successfully</font>
379/// <font color="#AAAAAA">2022-02-15T18:40:14.290157Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: yak_events: </font><i>yak</i><font color="#AAAAAA">=2 </font><i>shaved</i><font color="#AAAAAA">=true</font>
380/// <font color="#AAAAAA">2022-02-15T18:40:14.290174Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: </font><i>yaks_shaved</i><font color="#AAAAAA">=2</font>
381/// <font color="#AAAAAA">2022-02-15T18:40:14.290198Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: hello! I&apos;m gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">=&quot;yay!&quot;</font>
382/// <font color="#AAAAAA">2022-02-15T18:40:14.290222Z </font><font color="#C4A000"> WARN</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">:</font><b>shave{</b><i>yak</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: could not locate yak</font>
383/// <font color="#AAAAAA">2022-02-15T18:40:14.290247Z </font><font color="#3465A4">DEBUG</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: yak_events: </font><i>yak</i><font color="#AAAAAA">=3 </font><i>shaved</i><font color="#AAAAAA">=false</font>
384/// <font color="#AAAAAA">2022-02-15T18:40:14.290268Z </font><font color="#CC0000">ERROR</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: failed to shave yak </font><i>yak</i><font color="#AAAAAA">=3 </font><i>error</i><font color="#AAAAAA">=missing yak </font><i>error.sources</i><font color="#AAAAAA">=[out of space, out of cash]</font>
385/// <font color="#AAAAAA">2022-02-15T18:40:14.290287Z </font><font color="#75507B">TRACE</font> <b>shaving_yaks{</b><i>yaks</i><font color="#AAAAAA">=3</font><b>}</b><font color="#AAAAAA">: fmt::yak_shave: </font><i>yaks_shaved</i><font color="#AAAAAA">=2</font>
386/// <font color="#AAAAAA">2022-02-15T18:40:14.290309Z </font><font color="#4E9A06"> INFO</font> fmt: yak shaving completed. <i>all_yaks_shaved</i><font color="#AAAAAA">=false</font>
387/// </pre>
f035d41b
XL
388#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
389pub struct Full;
390
391/// A pre-configured event formatter.
392///
393/// You will usually want to use this as the `FormatEvent` for a `FmtSubscriber`.
394///
395/// The default logging format, [`Full`] includes all fields in each event and its containing
5e7ed085
FG
396/// spans. The [`Compact`] logging format is intended to produce shorter log
397/// lines; it displays each event's fields, along with fields from the current
398/// span context, but other information is abbreviated. The [`Pretty`] logging
399/// format is an extra-verbose, multi-line human-readable logging format
400/// intended for use in development.
f035d41b
XL
401#[derive(Debug, Clone)]
402pub struct Format<F = Full, T = SystemTime> {
403 format: F,
404 pub(crate) timer: T,
a2a8927a 405 pub(crate) ansi: Option<bool>,
17df50a5 406 pub(crate) display_timestamp: bool,
f035d41b
XL
407 pub(crate) display_target: bool,
408 pub(crate) display_level: bool,
3dfed10e
XL
409 pub(crate) display_thread_id: bool,
410 pub(crate) display_thread_name: bool,
5099ac24
FG
411 pub(crate) display_filename: bool,
412 pub(crate) display_line_number: bool,
f035d41b
XL
413}
414
a2a8927a
XL
415// === impl Writer ===
416
417impl<'writer> Writer<'writer> {
418 // TODO(eliza): consider making this a public API?
419 // We may not want to do that if we choose to expose specialized
420 // constructors instead (e.g. `from_string` that stores whether the string
421 // is empty...?)
422 pub(crate) fn new(writer: &'writer mut impl fmt::Write) -> Self {
423 Self {
424 writer: writer as &mut dyn fmt::Write,
425 is_ansi: false,
426 }
427 }
428
429 // TODO(eliza): consider making this a public API?
430 pub(crate) fn with_ansi(self, is_ansi: bool) -> Self {
431 Self { is_ansi, ..self }
432 }
433
434 /// Return a new `Writer` that mutably borrows `self`.
435 ///
436 /// This can be used to temporarily borrow a `Writer` to pass a new `Writer`
437 /// to a function that takes a `Writer` by value, allowing the original writer
438 /// to still be used once that function returns.
439 pub fn by_ref(&mut self) -> Writer<'_> {
440 let is_ansi = self.is_ansi;
441 Writer {
442 writer: self as &mut dyn fmt::Write,
443 is_ansi,
444 }
445 }
446
447 /// Writes a string slice into this `Writer`, returning whether the write succeeded.
448 ///
449 /// This method can only succeed if the entire string slice was successfully
450 /// written, and this method will not return until all data has been written
451 /// or an error occurs.
452 ///
453 /// This is identical to calling the [`write_str` method] from the `Writer`'s
454 /// [`std::fmt::Write`] implementation. However, it is also provided as an
455 /// inherent method, so that `Writer`s can be used without needing to import the
456 /// [`std::fmt::Write`] trait.
457 ///
458 /// # Errors
459 ///
460 /// This function will return an instance of [`std::fmt::Error`] on error.
461 ///
462 /// [`write_str` method]: std::fmt::Write::write_str
463 #[inline]
464 pub fn write_str(&mut self, s: &str) -> fmt::Result {
465 self.writer.write_str(s)
466 }
467
468 /// Writes a [`char`] into this writer, returning whether the write succeeded.
469 ///
470 /// A single [`char`] may be encoded as more than one byte.
471 /// This method can only succeed if the entire byte sequence was successfully
472 /// written, and this method will not return until all data has been
473 /// written or an error occurs.
474 ///
475 /// This is identical to calling the [`write_char` method] from the `Writer`'s
476 /// [`std::fmt::Write`] implementation. However, it is also provided as an
477 /// inherent method, so that `Writer`s can be used without needing to import the
478 /// [`std::fmt::Write`] trait.
479 ///
480 /// # Errors
481 ///
482 /// This function will return an instance of [`std::fmt::Error`] on error.
483 ///
484 /// [`write_char` method]: std::fmt::Write::write_char
485 #[inline]
486 pub fn write_char(&mut self, c: char) -> fmt::Result {
487 self.writer.write_char(c)
488 }
489
490 /// Glue for usage of the [`write!`] macro with `Writer`s.
491 ///
492 /// This method should generally not be invoked manually, but rather through
493 /// the [`write!`] macro itself.
494 ///
495 /// This is identical to calling the [`write_fmt` method] from the `Writer`'s
496 /// [`std::fmt::Write`] implementation. However, it is also provided as an
497 /// inherent method, so that `Writer`s can be used with the [`write!` macro]
498 /// without needing to import the
499 /// [`std::fmt::Write`] trait.
500 ///
501 /// [`write_fmt` method]: std::fmt::Write::write_fmt
502 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
503 self.writer.write_fmt(args)
504 }
505
506 /// Returns `true` if [ANSI escape codes] may be used to add colors
507 /// and other formatting when writing to this `Writer`.
508 ///
509 /// If this returns `false`, formatters should not emit ANSI escape codes.
510 ///
511 /// [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code
512 pub fn has_ansi_escapes(&self) -> bool {
513 self.is_ansi
514 }
515
516 pub(in crate::fmt::format) fn bold(&self) -> Style {
517 #[cfg(feature = "ansi")]
518 {
519 if self.is_ansi {
520 return Style::new().bold();
521 }
522 }
523
524 Style::new()
525 }
526
527 pub(in crate::fmt::format) fn dimmed(&self) -> Style {
528 #[cfg(feature = "ansi")]
529 {
530 if self.is_ansi {
531 return Style::new().dimmed();
532 }
533 }
534
535 Style::new()
536 }
537
538 pub(in crate::fmt::format) fn italic(&self) -> Style {
539 #[cfg(feature = "ansi")]
540 {
541 if self.is_ansi {
542 return Style::new().italic();
543 }
544 }
545
546 Style::new()
547 }
548}
549
550impl fmt::Write for Writer<'_> {
551 #[inline]
552 fn write_str(&mut self, s: &str) -> fmt::Result {
553 Writer::write_str(self, s)
554 }
555
556 #[inline]
557 fn write_char(&mut self, c: char) -> fmt::Result {
558 Writer::write_char(self, c)
559 }
560
561 #[inline]
562 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
563 Writer::write_fmt(self, args)
564 }
565}
566
567impl fmt::Debug for Writer<'_> {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 f.debug_struct("Writer")
570 .field("writer", &format_args!("<&mut dyn fmt::Write>"))
571 .field("is_ansi", &self.is_ansi)
572 .finish()
573 }
574}
575
576// === impl Format ===
577
f035d41b
XL
578impl Default for Format<Full, SystemTime> {
579 fn default() -> Self {
580 Format {
581 format: Full,
582 timer: SystemTime,
a2a8927a 583 ansi: None,
17df50a5 584 display_timestamp: true,
f035d41b
XL
585 display_target: true,
586 display_level: true,
3dfed10e
XL
587 display_thread_id: false,
588 display_thread_name: false,
5099ac24
FG
589 display_filename: false,
590 display_line_number: false,
f035d41b
XL
591 }
592 }
593}
594
595impl<F, T> Format<F, T> {
596 /// Use a less verbose output format.
597 ///
598 /// See [`Compact`].
599 pub fn compact(self) -> Format<Compact, T> {
600 Format {
601 format: Compact,
602 timer: self.timer,
603 ansi: self.ansi,
604 display_target: self.display_target,
17df50a5 605 display_timestamp: self.display_timestamp,
f035d41b 606 display_level: self.display_level,
3dfed10e
XL
607 display_thread_id: self.display_thread_id,
608 display_thread_name: self.display_thread_name,
5099ac24
FG
609 display_filename: self.display_filename,
610 display_line_number: self.display_line_number,
f035d41b
XL
611 }
612 }
613
5869c6ff
XL
614 /// Use an excessively pretty, human-readable output format.
615 ///
616 /// See [`Pretty`].
617 ///
618 /// Note that this requires the "ansi" feature to be enabled.
cdc7bbd5
XL
619 ///
620 /// # Options
621 ///
622 /// [`Format::with_ansi`] can be used to disable ANSI terminal escape codes (which enable
623 /// formatting such as colors, bold, italic, etc) in event formatting. However, a field
624 /// formatter must be manually provided to avoid ANSI in the formatting of parent spans, like
625 /// so:
626 ///
627 /// ```
628 /// # use tracing_subscriber::fmt::format;
629 /// tracing_subscriber::fmt()
630 /// .pretty()
631 /// .with_ansi(false)
632 /// .fmt_fields(format::PrettyFields::new().with_ansi(false))
633 /// // ... other settings ...
634 /// .init();
635 /// ```
5869c6ff
XL
636 #[cfg(feature = "ansi")]
637 #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
638 pub fn pretty(self) -> Format<Pretty, T> {
639 Format {
640 format: Pretty::default(),
641 timer: self.timer,
642 ansi: self.ansi,
643 display_target: self.display_target,
17df50a5 644 display_timestamp: self.display_timestamp,
5869c6ff
XL
645 display_level: self.display_level,
646 display_thread_id: self.display_thread_id,
647 display_thread_name: self.display_thread_name,
5099ac24
FG
648 display_filename: true,
649 display_line_number: true,
5869c6ff
XL
650 }
651 }
652
f035d41b
XL
653 /// Use the full JSON format.
654 ///
655 /// The full format includes fields from all entered spans.
656 ///
657 /// # Example Output
658 ///
659 /// ```ignore,json
660 /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}}
661 /// ```
662 ///
663 /// # Options
664 ///
665 /// - [`Format::flatten_event`] can be used to enable flattening event fields into the root
666 /// object.
667 ///
668 /// [`Format::flatten_event`]: #method.flatten_event
669 #[cfg(feature = "json")]
670 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
671 pub fn json(self) -> Format<Json, T> {
672 Format {
673 format: Json::default(),
674 timer: self.timer,
675 ansi: self.ansi,
676 display_target: self.display_target,
17df50a5 677 display_timestamp: self.display_timestamp,
f035d41b 678 display_level: self.display_level,
3dfed10e
XL
679 display_thread_id: self.display_thread_id,
680 display_thread_name: self.display_thread_name,
5099ac24
FG
681 display_filename: self.display_filename,
682 display_line_number: self.display_line_number,
f035d41b
XL
683 }
684 }
685
686 /// Use the given [`timer`] for log message timestamps.
687 ///
17df50a5 688 /// See [`time` module] for the provided timer implementations.
f035d41b 689 ///
a2a8927a
XL
690 /// Note that using the `"time"` feature flag enables the
691 /// additional time formatters [`UtcTime`] and [`LocalTime`], which use the
692 /// [`time` crate] to provide more sophisticated timestamp formatting
693 /// options.
f035d41b 694 ///
17df50a5
XL
695 /// [`timer`]: super::time::FormatTime
696 /// [`time` module]: mod@super::time
a2a8927a
XL
697 /// [`UtcTime`]: super::time::UtcTime
698 /// [`LocalTime`]: super::time::LocalTime
699 /// [`time` crate]: https://docs.rs/time/0.3
f035d41b
XL
700 pub fn with_timer<T2>(self, timer: T2) -> Format<F, T2> {
701 Format {
702 format: self.format,
703 timer,
704 ansi: self.ansi,
705 display_target: self.display_target,
17df50a5 706 display_timestamp: self.display_timestamp,
f035d41b 707 display_level: self.display_level,
3dfed10e
XL
708 display_thread_id: self.display_thread_id,
709 display_thread_name: self.display_thread_name,
5099ac24
FG
710 display_filename: self.display_filename,
711 display_line_number: self.display_line_number,
f035d41b
XL
712 }
713 }
714
715 /// Do not emit timestamps with log messages.
716 pub fn without_time(self) -> Format<F, ()> {
717 Format {
718 format: self.format,
719 timer: (),
720 ansi: self.ansi,
17df50a5 721 display_timestamp: false,
f035d41b
XL
722 display_target: self.display_target,
723 display_level: self.display_level,
3dfed10e
XL
724 display_thread_id: self.display_thread_id,
725 display_thread_name: self.display_thread_name,
5099ac24
FG
726 display_filename: self.display_filename,
727 display_line_number: self.display_line_number,
f035d41b
XL
728 }
729 }
730
731 /// Enable ANSI terminal colors for formatted output.
732 pub fn with_ansi(self, ansi: bool) -> Format<F, T> {
a2a8927a
XL
733 Format {
734 ansi: Some(ansi),
735 ..self
736 }
f035d41b
XL
737 }
738
739 /// Sets whether or not an event's target is displayed.
740 pub fn with_target(self, display_target: bool) -> Format<F, T> {
741 Format {
742 display_target,
743 ..self
744 }
745 }
746
747 /// Sets whether or not an event's level is displayed.
748 pub fn with_level(self, display_level: bool) -> Format<F, T> {
749 Format {
750 display_level,
751 ..self
752 }
753 }
3dfed10e
XL
754
755 /// Sets whether or not the [thread ID] of the current thread is displayed
756 /// when formatting events
757 ///
758 /// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html
759 pub fn with_thread_ids(self, display_thread_id: bool) -> Format<F, T> {
760 Format {
761 display_thread_id,
762 ..self
763 }
764 }
765
766 /// Sets whether or not the [name] of the current thread is displayed
767 /// when formatting events
768 ///
769 /// [name]: https://doc.rust-lang.org/stable/std/thread/index.html#naming-threads
770 pub fn with_thread_names(self, display_thread_name: bool) -> Format<F, T> {
771 Format {
772 display_thread_name,
773 ..self
774 }
775 }
17df50a5 776
5099ac24
FG
777 /// Sets whether or not an event's [source code file path][file] is
778 /// displayed.
779 ///
780 /// [file]: tracing_core::Metadata::file
781 pub fn with_file(self, display_filename: bool) -> Format<F, T> {
782 Format {
783 display_filename,
784 ..self
785 }
786 }
787
788 /// Sets whether or not an event's [source code line number][line] is
789 /// displayed.
790 ///
791 /// [line]: tracing_core::Metadata::line
792 pub fn with_line_number(self, display_line_number: bool) -> Format<F, T> {
793 Format {
794 display_line_number,
795 ..self
796 }
797 }
798
799 /// Sets whether or not the source code location from which an event
800 /// originated is displayed.
801 ///
802 /// This is equivalent to calling [`Format::with_file`] and
803 /// [`Format::with_line_number`] with the same value.
804 pub fn with_source_location(self, display_location: bool) -> Self {
805 self.with_line_number(display_location)
806 .with_file(display_location)
807 }
808
17df50a5 809 #[inline]
a2a8927a 810 fn format_timestamp(&self, writer: &mut Writer<'_>) -> fmt::Result
17df50a5
XL
811 where
812 T: FormatTime,
813 {
814 // If timestamps are disabled, do nothing.
815 if !self.display_timestamp {
816 return Ok(());
817 }
818
819 // If ANSI color codes are enabled, format the timestamp with ANSI
820 // colors.
821 #[cfg(feature = "ansi")]
822 {
a2a8927a 823 if writer.has_ansi_escapes() {
17df50a5
XL
824 let style = Style::new().dimmed();
825 write!(writer, "{}", style.prefix())?;
5099ac24
FG
826
827 // If getting the timestamp failed, don't bail --- only bail on
828 // formatting errors.
829 if self.timer.format_time(writer).is_err() {
830 writer.write_str("<unknown time>")?;
831 }
832
17df50a5
XL
833 write!(writer, "{} ", style.suffix())?;
834 return Ok(());
835 }
836 }
837
838 // Otherwise, just format the timestamp without ANSI formatting.
5099ac24
FG
839 // If getting the timestamp failed, don't bail --- only bail on
840 // formatting errors.
841 if self.timer.format_time(writer).is_err() {
842 writer.write_str("<unknown time>")?;
843 }
17df50a5
XL
844 writer.write_char(' ')
845 }
f035d41b
XL
846}
847
848#[cfg(feature = "json")]
849#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
850impl<T> Format<Json, T> {
851 /// Use the full JSON format with the event's event fields flattened.
852 ///
853 /// # Example Output
854 ///
855 /// ```ignore,json
856 /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate", "message":"some message", "key": "value"}
857 /// ```
858 /// See [`Json`](../format/struct.Json.html).
859 #[cfg(feature = "json")]
860 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
861 pub fn flatten_event(mut self, flatten_event: bool) -> Format<Json, T> {
862 self.format.flatten_event(flatten_event);
863 self
864 }
3dfed10e
XL
865
866 /// Sets whether or not the formatter will include the current span in
867 /// formatted events.
868 ///
869 /// See [`format::Json`](../fmt/format/struct.Json.html)
870 #[cfg(feature = "json")]
871 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
872 pub fn with_current_span(mut self, display_current_span: bool) -> Format<Json, T> {
873 self.format.with_current_span(display_current_span);
874 self
875 }
876
877 /// Sets whether or not the formatter will include a list (from root to
878 /// leaf) of all currently entered spans in formatted events.
879 ///
880 /// See [`format::Json`](../fmt/format/struct.Json.html)
881 #[cfg(feature = "json")]
882 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
883 pub fn with_span_list(mut self, display_span_list: bool) -> Format<Json, T> {
884 self.format.with_span_list(display_span_list);
885 self
886 }
f035d41b
XL
887}
888
889impl<S, N, T> FormatEvent<S, N> for Format<Full, T>
890where
891 S: Subscriber + for<'a> LookupSpan<'a>,
892 N: for<'a> FormatFields<'a> + 'static,
893 T: FormatTime,
894{
895 fn format_event(
896 &self,
897 ctx: &FmtContext<'_, S, N>,
a2a8927a 898 mut writer: Writer<'_>,
f035d41b
XL
899 event: &Event<'_>,
900 ) -> fmt::Result {
901 #[cfg(feature = "tracing-log")]
902 let normalized_meta = event.normalized_metadata();
903 #[cfg(feature = "tracing-log")]
904 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
905 #[cfg(not(feature = "tracing-log"))]
906 let meta = event.metadata();
17df50a5 907
a2a8927a
XL
908 // if the `Format` struct *also* has an ANSI color configuration,
909 // override the writer...the API for configuring ANSI color codes on the
910 // `Format` struct is deprecated, but we still need to honor those
911 // configurations.
912 if let Some(ansi) = self.ansi {
913 writer = writer.with_ansi(ansi);
914 }
915
916 self.format_timestamp(&mut writer)?;
f035d41b
XL
917
918 if self.display_level {
919 let fmt_level = {
920 #[cfg(feature = "ansi")]
921 {
a2a8927a 922 FmtLevel::new(meta.level(), writer.has_ansi_escapes())
f035d41b
XL
923 }
924 #[cfg(not(feature = "ansi"))]
925 {
926 FmtLevel::new(meta.level())
927 }
928 };
929 write!(writer, "{} ", fmt_level)?;
930 }
931
3dfed10e
XL
932 if self.display_thread_name {
933 let current_thread = std::thread::current();
934 match current_thread.name() {
935 Some(name) => {
936 write!(writer, "{} ", FmtThreadName::new(name))?;
937 }
938 // fall-back to thread id when name is absent and ids are not enabled
939 None if !self.display_thread_id => {
940 write!(writer, "{:0>2?} ", current_thread.id())?;
941 }
942 _ => {}
943 }
944 }
945
946 if self.display_thread_id {
947 write!(writer, "{:0>2?} ", std::thread::current().id())?;
948 }
949
a2a8927a
XL
950 let dimmed = writer.dimmed();
951
952 if let Some(scope) = ctx.event_scope() {
953 let bold = writer.bold();
954
955 let mut seen = false;
956
957 for span in scope.from_root() {
958 write!(writer, "{}", bold.paint(span.metadata().name()))?;
959 seen = true;
960
961 let ext = span.extensions();
962 if let Some(fields) = &ext.get::<FormattedFields<N>>() {
963 if !fields.is_empty() {
964 write!(writer, "{}{}{}", bold.paint("{"), fields, bold.paint("}"))?;
965 }
966 }
967 write!(writer, "{}", dimmed.paint(":"))?;
f035d41b 968 }
a2a8927a
XL
969
970 if seen {
971 writer.write_char(' ')?;
f035d41b
XL
972 }
973 };
974
f035d41b 975 if self.display_target {
a2a8927a
XL
976 write!(
977 writer,
978 "{}{} ",
979 dimmed.paint(meta.target()),
980 dimmed.paint(":")
981 )?;
f035d41b 982 }
a2a8927a 983
5099ac24
FG
984 let line_number = if self.display_line_number {
985 meta.line()
986 } else {
987 None
988 };
989
990 if self.display_filename {
991 if let Some(filename) = meta.file() {
992 write!(
993 writer,
994 "{}{}{}",
995 dimmed.paint(filename),
996 dimmed.paint(":"),
997 if line_number.is_some() { "" } else { " " }
998 )?;
999 }
1000 }
1001
1002 if let Some(line_number) = line_number {
1003 write!(
1004 writer,
1005 "{}{}:{} ",
1006 dimmed.prefix(),
1007 line_number,
1008 dimmed.suffix()
1009 )?;
1010 }
1011
a2a8927a 1012 ctx.format_fields(writer.by_ref(), event)?;
f035d41b
XL
1013 writeln!(writer)
1014 }
1015}
1016
1017impl<S, N, T> FormatEvent<S, N> for Format<Compact, T>
1018where
1019 S: Subscriber + for<'a> LookupSpan<'a>,
1020 N: for<'a> FormatFields<'a> + 'static,
1021 T: FormatTime,
1022{
1023 fn format_event(
1024 &self,
1025 ctx: &FmtContext<'_, S, N>,
a2a8927a 1026 mut writer: Writer<'_>,
f035d41b
XL
1027 event: &Event<'_>,
1028 ) -> fmt::Result {
1029 #[cfg(feature = "tracing-log")]
1030 let normalized_meta = event.normalized_metadata();
1031 #[cfg(feature = "tracing-log")]
1032 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1033 #[cfg(not(feature = "tracing-log"))]
1034 let meta = event.metadata();
17df50a5 1035
a2a8927a
XL
1036 // if the `Format` struct *also* has an ANSI color configuration,
1037 // override the writer...the API for configuring ANSI color codes on the
1038 // `Format` struct is deprecated, but we still need to honor those
1039 // configurations.
1040 if let Some(ansi) = self.ansi {
1041 writer = writer.with_ansi(ansi);
1042 }
1043
1044 self.format_timestamp(&mut writer)?;
f035d41b
XL
1045
1046 if self.display_level {
1047 let fmt_level = {
1048 #[cfg(feature = "ansi")]
1049 {
a2a8927a 1050 FmtLevel::new(meta.level(), writer.has_ansi_escapes())
f035d41b
XL
1051 }
1052 #[cfg(not(feature = "ansi"))]
1053 {
1054 FmtLevel::new(meta.level())
1055 }
1056 };
1057 write!(writer, "{} ", fmt_level)?;
1058 }
1059
3dfed10e
XL
1060 if self.display_thread_name {
1061 let current_thread = std::thread::current();
1062 match current_thread.name() {
1063 Some(name) => {
1064 write!(writer, "{} ", FmtThreadName::new(name))?;
1065 }
1066 // fall-back to thread id when name is absent and ids are not enabled
1067 None if !self.display_thread_id => {
1068 write!(writer, "{:0>2?} ", current_thread.id())?;
1069 }
1070 _ => {}
1071 }
1072 }
1073
1074 if self.display_thread_id {
1075 write!(writer, "{:0>2?} ", std::thread::current().id())?;
1076 }
1077
f035d41b
XL
1078 let fmt_ctx = {
1079 #[cfg(feature = "ansi")]
1080 {
a2a8927a 1081 FmtCtx::new(ctx, event.parent(), writer.has_ansi_escapes())
f035d41b
XL
1082 }
1083 #[cfg(not(feature = "ansi"))]
1084 {
1085 FmtCtx::new(&ctx, event.parent())
1086 }
1087 };
1088 write!(writer, "{}", fmt_ctx)?;
a2a8927a 1089
5099ac24
FG
1090 let bold = writer.bold();
1091 let dimmed = writer.dimmed();
1092
1093 let mut needs_space = false;
f035d41b 1094 if self.display_target {
5099ac24
FG
1095 write!(writer, "{}{}", bold.paint(meta.target()), dimmed.paint(":"))?;
1096 needs_space = true;
1097 }
1098
1099 if self.display_filename {
1100 if let Some(filename) = meta.file() {
1101 if self.display_target {
1102 writer.write_char(' ')?;
1103 }
1104 write!(writer, "{}{}", bold.paint(filename), dimmed.paint(":"))?;
1105 needs_space = true;
1106 }
1107 }
1108
1109 if self.display_line_number {
1110 if let Some(line_number) = meta.line() {
1111 write!(
1112 writer,
1113 "{}{}{}{}",
1114 bold.prefix(),
1115 line_number,
1116 bold.suffix(),
1117 dimmed.paint(":")
1118 )?;
1119 needs_space = true;
1120 }
1121 }
1122
1123 if needs_space {
1124 writer.write_char(' ')?;
f035d41b 1125 }
c295e0f8 1126
a2a8927a 1127 ctx.format_fields(writer.by_ref(), event)?;
c295e0f8 1128
a2a8927a
XL
1129 for span in ctx
1130 .event_scope()
1131 .into_iter()
04454e1e 1132 .flat_map(crate::registry::Scope::from_root)
a2a8927a 1133 {
c295e0f8
XL
1134 let exts = span.extensions();
1135 if let Some(fields) = exts.get::<FormattedFields<N>>() {
1136 if !fields.is_empty() {
a2a8927a 1137 write!(writer, " {}", dimmed.paint(&fields.fields))?;
c295e0f8 1138 }
f035d41b
XL
1139 }
1140 }
1141 writeln!(writer)
1142 }
1143}
1144
1145// === impl FormatFields ===
1146impl<'writer, M> FormatFields<'writer> for M
1147where
a2a8927a 1148 M: MakeOutput<Writer<'writer>, fmt::Result>,
f035d41b
XL
1149 M::Visitor: VisitFmt + VisitOutput<fmt::Result>,
1150{
a2a8927a 1151 fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result {
f035d41b
XL
1152 let mut v = self.make_visitor(writer);
1153 fields.record(&mut v);
1154 v.finish()
1155 }
1156}
a2a8927a 1157
f035d41b
XL
1158/// The default [`FormatFields`] implementation.
1159///
1160/// [`FormatFields`]: trait.FormatFields.html
1161#[derive(Debug)]
1162pub struct DefaultFields {
1163 // reserve the ability to add fields to this without causing a breaking
1164 // change in the future.
1165 _private: (),
1166}
1167
1168/// The [visitor] produced by [`DefaultFields`]'s [`MakeVisitor`] implementation.
1169///
a2a8927a
XL
1170/// [visitor]: super::super::field::Visit
1171/// [`MakeVisitor`]: super::super::field::MakeVisitor
1172#[derive(Debug)]
f035d41b 1173pub struct DefaultVisitor<'a> {
a2a8927a 1174 writer: Writer<'a>,
f035d41b
XL
1175 is_empty: bool,
1176 result: fmt::Result,
1177}
1178
1179impl DefaultFields {
1180 /// Returns a new default [`FormatFields`] implementation.
1181 ///
1182 /// [`FormatFields`]: trait.FormatFields.html
1183 pub fn new() -> Self {
1184 Self { _private: () }
1185 }
1186}
1187
1188impl Default for DefaultFields {
1189 fn default() -> Self {
1190 Self::new()
1191 }
1192}
1193
a2a8927a 1194impl<'a> MakeVisitor<Writer<'a>> for DefaultFields {
f035d41b
XL
1195 type Visitor = DefaultVisitor<'a>;
1196
1197 #[inline]
a2a8927a 1198 fn make_visitor(&self, target: Writer<'a>) -> Self::Visitor {
f035d41b
XL
1199 DefaultVisitor::new(target, true)
1200 }
1201}
1202
1203// === impl DefaultVisitor ===
1204
1205impl<'a> DefaultVisitor<'a> {
1206 /// Returns a new default visitor that formats to the provided `writer`.
1207 ///
1208 /// # Arguments
1209 /// - `writer`: the writer to format to.
1210 /// - `is_empty`: whether or not any fields have been previously written to
1211 /// that writer.
a2a8927a 1212 pub fn new(writer: Writer<'a>, is_empty: bool) -> Self {
f035d41b
XL
1213 Self {
1214 writer,
1215 is_empty,
1216 result: Ok(()),
1217 }
1218 }
1219
1220 fn maybe_pad(&mut self) {
1221 if self.is_empty {
1222 self.is_empty = false;
1223 } else {
1224 self.result = write!(self.writer, " ");
1225 }
1226 }
1227}
1228
1229impl<'a> field::Visit for DefaultVisitor<'a> {
1230 fn record_str(&mut self, field: &Field, value: &str) {
1231 if self.result.is_err() {
1232 return;
1233 }
1234
1235 if field.name() == "message" {
1236 self.record_debug(field, &format_args!("{}", value))
1237 } else {
1238 self.record_debug(field, &value)
1239 }
1240 }
1241
1242 fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
1243 if let Some(source) = value.source() {
a2a8927a 1244 let italic = self.writer.italic();
c295e0f8
XL
1245 self.record_debug(
1246 field,
a2a8927a
XL
1247 &format_args!(
1248 "{} {}{}{}{}",
1249 value,
1250 italic.paint(field.name()),
1251 italic.paint(".sources"),
1252 self.writer.dimmed().paint("="),
1253 ErrorSourceList(source)
1254 ),
c295e0f8 1255 )
f035d41b
XL
1256 } else {
1257 self.record_debug(field, &format_args!("{}", value))
1258 }
1259 }
1260
1261 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
1262 if self.result.is_err() {
1263 return;
1264 }
1265
1266 self.maybe_pad();
1267 self.result = match field.name() {
1268 "message" => write!(self.writer, "{:?}", value),
1269 // Skip fields that are actually log metadata that have already been handled
1270 #[cfg(feature = "tracing-log")]
1271 name if name.starts_with("log.") => Ok(()),
a2a8927a
XL
1272 name if name.starts_with("r#") => write!(
1273 self.writer,
1274 "{}{}{:?}",
1275 self.writer.italic().paint(&name[2..]),
1276 self.writer.dimmed().paint("="),
1277 value
1278 ),
1279 name => write!(
1280 self.writer,
1281 "{}{}{:?}",
1282 self.writer.italic().paint(name),
1283 self.writer.dimmed().paint("="),
1284 value
1285 ),
f035d41b
XL
1286 };
1287 }
1288}
1289
1290impl<'a> crate::field::VisitOutput<fmt::Result> for DefaultVisitor<'a> {
1291 fn finish(self) -> fmt::Result {
1292 self.result
1293 }
1294}
1295
1296impl<'a> crate::field::VisitFmt for DefaultVisitor<'a> {
1297 fn writer(&mut self) -> &mut dyn fmt::Write {
a2a8927a 1298 &mut self.writer
f035d41b
XL
1299 }
1300}
1301
c295e0f8
XL
1302/// Renders an error into a list of sources, *including* the error
1303struct ErrorSourceList<'a>(&'a (dyn std::error::Error + 'static));
1304
1305impl<'a> Display for ErrorSourceList<'a> {
1306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1307 let mut list = f.debug_list();
1308 let mut curr = Some(self.0);
1309 while let Some(curr_err) = curr {
1310 list.entry(&format_args!("{}", curr_err));
1311 curr = curr_err.source();
1312 }
1313 list.finish()
1314 }
1315}
1316
f035d41b
XL
1317struct FmtCtx<'a, S, N> {
1318 ctx: &'a FmtContext<'a, S, N>,
1319 span: Option<&'a span::Id>,
1320 #[cfg(feature = "ansi")]
1321 ansi: bool,
1322}
1323
1324impl<'a, S, N: 'a> FmtCtx<'a, S, N>
1325where
1326 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
1327 N: for<'writer> FormatFields<'writer> + 'static,
1328{
1329 #[cfg(feature = "ansi")]
1330 pub(crate) fn new(
1331 ctx: &'a FmtContext<'_, S, N>,
1332 span: Option<&'a span::Id>,
1333 ansi: bool,
1334 ) -> Self {
136023e0 1335 Self { ctx, span, ansi }
f035d41b
XL
1336 }
1337
1338 #[cfg(not(feature = "ansi"))]
1339 pub(crate) fn new(ctx: &'a FmtContext<'_, S, N>, span: Option<&'a span::Id>) -> Self {
1340 Self { ctx, span }
1341 }
1342
1343 fn bold(&self) -> Style {
1344 #[cfg(feature = "ansi")]
1345 {
1346 if self.ansi {
1347 return Style::new().bold();
1348 }
1349 }
1350
1351 Style::new()
1352 }
1353}
1354
1355impl<'a, S, N: 'a> fmt::Display for FmtCtx<'a, S, N>
1356where
1357 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
1358 N: for<'writer> FormatFields<'writer> + 'static,
1359{
1360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1361 let bold = self.bold();
1362 let mut seen = false;
1363
1364 let span = self
1365 .span
c295e0f8 1366 .and_then(|id| self.ctx.ctx.span(id))
f035d41b
XL
1367 .or_else(|| self.ctx.ctx.lookup_current());
1368
136023e0 1369 let scope = span.into_iter().flat_map(|span| span.scope().from_root());
f035d41b
XL
1370
1371 for span in scope {
1372 seen = true;
1373 write!(f, "{}:", bold.paint(span.metadata().name()))?;
1374 }
1375
1376 if seen {
1377 f.write_char(' ')?;
1378 }
1379 Ok(())
1380 }
1381}
1382
f035d41b
XL
1383#[cfg(not(feature = "ansi"))]
1384struct Style;
1385
1386#[cfg(not(feature = "ansi"))]
1387impl Style {
1388 fn new() -> Self {
1389 Style
1390 }
a2a8927a
XL
1391
1392 fn bold(self) -> Self {
1393 self
1394 }
1395
f035d41b
XL
1396 fn paint(&self, d: impl fmt::Display) -> impl fmt::Display {
1397 d
1398 }
5099ac24
FG
1399
1400 fn prefix(&self) -> impl fmt::Display {
1401 ""
1402 }
1403
1404 fn suffix(&self) -> impl fmt::Display {
1405 ""
1406 }
f035d41b
XL
1407}
1408
3dfed10e
XL
1409struct FmtThreadName<'a> {
1410 name: &'a str,
1411}
1412
1413impl<'a> FmtThreadName<'a> {
1414 pub(crate) fn new(name: &'a str) -> Self {
1415 Self { name }
1416 }
1417}
1418
1419impl<'a> fmt::Display for FmtThreadName<'a> {
1420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1421 use std::sync::atomic::{
1422 AtomicUsize,
1423 Ordering::{AcqRel, Acquire, Relaxed},
1424 };
1425
1426 // Track the longest thread name length we've seen so far in an atomic,
1427 // so that it can be updated by any thread.
1428 static MAX_LEN: AtomicUsize = AtomicUsize::new(0);
1429 let len = self.name.len();
1430 // Snapshot the current max thread name length.
1431 let mut max_len = MAX_LEN.load(Relaxed);
1432
1433 while len > max_len {
1434 // Try to set a new max length, if it is still the value we took a
1435 // snapshot of.
1436 match MAX_LEN.compare_exchange(max_len, len, AcqRel, Acquire) {
1437 // We successfully set the new max value
1438 Ok(_) => break,
1439 // Another thread set a new max value since we last observed
1440 // it! It's possible that the new length is actually longer than
1441 // ours, so we'll loop again and check whether our length is
1442 // still the longest. If not, we'll just use the newer value.
1443 Err(actual) => max_len = actual,
1444 }
1445 }
1446
1447 // pad thread name using `max_len`
1448 write!(f, "{:>width$}", self.name, width = max_len)
1449 }
1450}
1451
f035d41b
XL
1452struct FmtLevel<'a> {
1453 level: &'a Level,
1454 #[cfg(feature = "ansi")]
1455 ansi: bool,
1456}
1457
1458impl<'a> FmtLevel<'a> {
1459 #[cfg(feature = "ansi")]
1460 pub(crate) fn new(level: &'a Level, ansi: bool) -> Self {
1461 Self { level, ansi }
1462 }
1463
1464 #[cfg(not(feature = "ansi"))]
1465 pub(crate) fn new(level: &'a Level) -> Self {
1466 Self { level }
1467 }
1468}
1469
1470const TRACE_STR: &str = "TRACE";
1471const DEBUG_STR: &str = "DEBUG";
1472const INFO_STR: &str = " INFO";
1473const WARN_STR: &str = " WARN";
1474const ERROR_STR: &str = "ERROR";
1475
1476#[cfg(not(feature = "ansi"))]
1477impl<'a> fmt::Display for FmtLevel<'a> {
1478 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479 match *self.level {
1480 Level::TRACE => f.pad(TRACE_STR),
1481 Level::DEBUG => f.pad(DEBUG_STR),
1482 Level::INFO => f.pad(INFO_STR),
1483 Level::WARN => f.pad(WARN_STR),
1484 Level::ERROR => f.pad(ERROR_STR),
1485 }
1486 }
1487}
1488
1489#[cfg(feature = "ansi")]
1490impl<'a> fmt::Display for FmtLevel<'a> {
1491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1492 if self.ansi {
1493 match *self.level {
1494 Level::TRACE => write!(f, "{}", Colour::Purple.paint(TRACE_STR)),
1495 Level::DEBUG => write!(f, "{}", Colour::Blue.paint(DEBUG_STR)),
1496 Level::INFO => write!(f, "{}", Colour::Green.paint(INFO_STR)),
1497 Level::WARN => write!(f, "{}", Colour::Yellow.paint(WARN_STR)),
1498 Level::ERROR => write!(f, "{}", Colour::Red.paint(ERROR_STR)),
1499 }
1500 } else {
1501 match *self.level {
1502 Level::TRACE => f.pad(TRACE_STR),
1503 Level::DEBUG => f.pad(DEBUG_STR),
1504 Level::INFO => f.pad(INFO_STR),
1505 Level::WARN => f.pad(WARN_STR),
1506 Level::ERROR => f.pad(ERROR_STR),
1507 }
1508 }
1509 }
1510}
1511
1512// === impl FieldFn ===
1513
a2a8927a 1514impl<'a, F> MakeVisitor<Writer<'a>> for FieldFn<F>
f035d41b 1515where
a2a8927a 1516 F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone,
f035d41b
XL
1517{
1518 type Visitor = FieldFnVisitor<'a, F>;
1519
a2a8927a 1520 fn make_visitor(&self, writer: Writer<'a>) -> Self::Visitor {
f035d41b
XL
1521 FieldFnVisitor {
1522 writer,
1523 f: self.0.clone(),
1524 result: Ok(()),
1525 }
1526 }
1527}
1528
1529impl<'a, F> Visit for FieldFnVisitor<'a, F>
1530where
a2a8927a 1531 F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result,
f035d41b
XL
1532{
1533 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
1534 if self.result.is_ok() {
1535 self.result = (self.f)(&mut self.writer, field, value)
1536 }
1537 }
1538}
1539
1540impl<'a, F> VisitOutput<fmt::Result> for FieldFnVisitor<'a, F>
1541where
a2a8927a 1542 F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result,
f035d41b
XL
1543{
1544 fn finish(self) -> fmt::Result {
1545 self.result
1546 }
1547}
1548
1549impl<'a, F> VisitFmt for FieldFnVisitor<'a, F>
1550where
a2a8927a 1551 F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result,
f035d41b
XL
1552{
1553 fn writer(&mut self) -> &mut dyn fmt::Write {
a2a8927a 1554 &mut self.writer
f035d41b
XL
1555 }
1556}
1557
1558impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> {
1559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1560 f.debug_struct("FieldFnVisitor")
a2a8927a
XL
1561 .field("f", &format_args!("{}", std::any::type_name::<F>()))
1562 .field("writer", &self.writer)
f035d41b
XL
1563 .field("result", &self.result)
1564 .finish()
1565 }
1566}
1567
1568// === printing synthetic Span events ===
1569
1570/// Configures what points in the span lifecycle are logged as events.
1571///
1572/// See also [`with_span_events`](../struct.SubscriberBuilder.html#method.with_span_events).
1573#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
cdc7bbd5 1574pub struct FmtSpan(u8);
f035d41b
XL
1575
1576impl FmtSpan {
cdc7bbd5
XL
1577 /// one event when span is created
1578 pub const NEW: FmtSpan = FmtSpan(1 << 0);
1579 /// one event per enter of a span
1580 pub const ENTER: FmtSpan = FmtSpan(1 << 1);
1581 /// one event per exit of a span
1582 pub const EXIT: FmtSpan = FmtSpan(1 << 2);
1583 /// one event when the span is dropped
1584 pub const CLOSE: FmtSpan = FmtSpan(1 << 3);
1585
f035d41b 1586 /// spans are ignored (this is the default)
cdc7bbd5 1587 pub const NONE: FmtSpan = FmtSpan(0);
f035d41b 1588 /// one event per enter/exit of a span
cdc7bbd5 1589 pub const ACTIVE: FmtSpan = FmtSpan(FmtSpan::ENTER.0 | FmtSpan::EXIT.0);
f035d41b 1590 /// events at all points (new, enter, exit, drop)
cdc7bbd5
XL
1591 pub const FULL: FmtSpan =
1592 FmtSpan(FmtSpan::NEW.0 | FmtSpan::ENTER.0 | FmtSpan::EXIT.0 | FmtSpan::CLOSE.0);
1593
1594 /// Check whether or not a certain flag is set for this [`FmtSpan`]
1595 fn contains(&self, other: FmtSpan) -> bool {
1596 self.clone() & other.clone() == other
1597 }
f035d41b
XL
1598}
1599
cdc7bbd5
XL
1600macro_rules! impl_fmt_span_bit_op {
1601 ($trait:ident, $func:ident, $op:tt) => {
1602 impl std::ops::$trait for FmtSpan {
1603 type Output = FmtSpan;
1604
1605 fn $func(self, rhs: Self) -> Self::Output {
1606 FmtSpan(self.0 $op rhs.0)
1607 }
1608 }
1609 };
1610}
1611
1612macro_rules! impl_fmt_span_bit_assign_op {
1613 ($trait:ident, $func:ident, $op:tt) => {
1614 impl std::ops::$trait for FmtSpan {
1615 fn $func(&mut self, rhs: Self) {
1616 *self = FmtSpan(self.0 $op rhs.0)
1617 }
1618 }
1619 };
1620}
1621
1622impl_fmt_span_bit_op!(BitAnd, bitand, &);
1623impl_fmt_span_bit_op!(BitOr, bitor, |);
1624impl_fmt_span_bit_op!(BitXor, bitxor, ^);
1625
1626impl_fmt_span_bit_assign_op!(BitAndAssign, bitand_assign, &);
1627impl_fmt_span_bit_assign_op!(BitOrAssign, bitor_assign, |);
1628impl_fmt_span_bit_assign_op!(BitXorAssign, bitxor_assign, ^);
1629
f035d41b
XL
1630impl Debug for FmtSpan {
1631 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
cdc7bbd5
XL
1632 let mut wrote_flag = false;
1633 let mut write_flags = |flag, flag_str| -> fmt::Result {
1634 if self.contains(flag) {
1635 if wrote_flag {
1636 f.write_str(" | ")?;
1637 }
1638
1639 f.write_str(flag_str)?;
1640 wrote_flag = true;
1641 }
1642
1643 Ok(())
1644 };
1645
1646 if FmtSpan::NONE | self.clone() == FmtSpan::NONE {
1647 f.write_str("FmtSpan::NONE")?;
1648 } else {
1649 write_flags(FmtSpan::NEW, "FmtSpan::NEW")?;
1650 write_flags(FmtSpan::ENTER, "FmtSpan::ENTER")?;
1651 write_flags(FmtSpan::EXIT, "FmtSpan::EXIT")?;
1652 write_flags(FmtSpan::CLOSE, "FmtSpan::CLOSE")?;
f035d41b 1653 }
f035d41b 1654
cdc7bbd5
XL
1655 Ok(())
1656 }
f035d41b
XL
1657}
1658
1659pub(super) struct FmtSpanConfig {
1660 pub(super) kind: FmtSpan,
1661 pub(super) fmt_timing: bool,
1662}
1663
1664impl FmtSpanConfig {
1665 pub(super) fn without_time(self) -> Self {
1666 Self {
1667 kind: self.kind,
1668 fmt_timing: false,
1669 }
1670 }
1671 pub(super) fn with_kind(self, kind: FmtSpan) -> Self {
1672 Self {
1673 kind,
1674 fmt_timing: self.fmt_timing,
1675 }
1676 }
1677 pub(super) fn trace_new(&self) -> bool {
cdc7bbd5
XL
1678 self.kind.contains(FmtSpan::NEW)
1679 }
1680 pub(super) fn trace_enter(&self) -> bool {
1681 self.kind.contains(FmtSpan::ENTER)
f035d41b 1682 }
cdc7bbd5
XL
1683 pub(super) fn trace_exit(&self) -> bool {
1684 self.kind.contains(FmtSpan::EXIT)
f035d41b
XL
1685 }
1686 pub(super) fn trace_close(&self) -> bool {
cdc7bbd5 1687 self.kind.contains(FmtSpan::CLOSE)
f035d41b
XL
1688 }
1689}
1690
1691impl Debug for FmtSpanConfig {
1692 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1693 self.kind.fmt(f)
1694 }
1695}
1696
1697impl Default for FmtSpanConfig {
1698 fn default() -> Self {
1699 Self {
1700 kind: FmtSpan::NONE,
1701 fmt_timing: true,
1702 }
1703 }
1704}
1705
f035d41b
XL
1706pub(super) struct TimingDisplay(pub(super) u64);
1707impl Display for TimingDisplay {
1708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1709 let mut t = self.0 as f64;
1710 for unit in ["ns", "µs", "ms", "s"].iter() {
1711 if t < 10.0 {
1712 return write!(f, "{:.2}{}", t, unit);
1713 } else if t < 100.0 {
1714 return write!(f, "{:.1}{}", t, unit);
1715 } else if t < 1000.0 {
1716 return write!(f, "{:.0}{}", t, unit);
1717 }
1718 t /= 1000.0;
1719 }
1720 write!(f, "{:.0}s", t * 1000.0)
1721 }
1722}
1723
1724#[cfg(test)]
1725pub(super) mod test {
a2a8927a
XL
1726 use crate::fmt::{test::MockMakeWriter, time::FormatTime};
1727 use tracing::{
1728 self,
1729 dispatcher::{set_default, Dispatch},
1730 subscriber::with_default,
1731 };
f035d41b 1732
a2a8927a 1733 use super::*;
5099ac24
FG
1734
1735 use regex::Regex;
1736 use std::{fmt, path::Path};
f035d41b
XL
1737
1738 pub(crate) struct MockTime;
1739 impl FormatTime for MockTime {
a2a8927a 1740 fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
f035d41b
XL
1741 write!(w, "fake time")
1742 }
1743 }
1744
17df50a5
XL
1745 #[test]
1746 fn disable_everything() {
1747 // This test reproduces https://github.com/tokio-rs/tracing/issues/1354
a2a8927a 1748 let make_writer = MockMakeWriter::default();
17df50a5 1749 let subscriber = crate::fmt::Subscriber::builder()
a2a8927a 1750 .with_writer(make_writer.clone())
17df50a5
XL
1751 .without_time()
1752 .with_level(false)
1753 .with_target(false)
1754 .with_thread_ids(false)
1755 .with_thread_names(false);
1756 #[cfg(feature = "ansi")]
1757 let subscriber = subscriber.with_ansi(false);
5099ac24 1758 assert_info_hello(subscriber, make_writer, "hello\n")
17df50a5
XL
1759 }
1760
a2a8927a
XL
1761 fn test_ansi<T>(
1762 is_ansi: bool,
1763 expected: &str,
1764 builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>,
1765 ) where
1766 Format<T, MockTime>: FormatEvent<crate::Registry, DefaultFields>,
1767 T: Send + Sync + 'static,
1768 {
1769 let make_writer = MockMakeWriter::default();
1770 let subscriber = builder
1771 .with_writer(make_writer.clone())
1772 .with_ansi(is_ansi)
1773 .with_timer(MockTime);
1774 run_test(subscriber, make_writer, expected)
f035d41b
XL
1775 }
1776
a2a8927a
XL
1777 #[cfg(not(feature = "ansi"))]
1778 fn test_without_ansi<T>(
1779 expected: &str,
1780 builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>,
1781 ) where
1782 Format<T, MockTime>: FormatEvent<crate::Registry, DefaultFields>,
1783 T: Send + Sync,
1784 {
1785 let make_writer = MockMakeWriter::default();
1786 let subscriber = builder.with_writer(make_writer).with_timer(MockTime);
1787 run_test(subscriber, make_writer, expected)
f035d41b
XL
1788 }
1789
a2a8927a
XL
1790 fn test_without_level<T>(
1791 expected: &str,
1792 builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>,
1793 ) where
1794 Format<T, MockTime>: FormatEvent<crate::Registry, DefaultFields>,
1795 T: Send + Sync + 'static,
1796 {
1797 let make_writer = MockMakeWriter::default();
1798 let subscriber = builder
1799 .with_writer(make_writer.clone())
1800 .with_level(false)
1801 .with_ansi(false)
1802 .with_timer(MockTime);
1803 run_test(subscriber, make_writer, expected);
1804 }
f035d41b 1805
5099ac24
FG
1806 #[test]
1807 fn with_line_number_and_file_name() {
1808 let make_writer = MockMakeWriter::default();
1809 let subscriber = crate::fmt::Subscriber::builder()
1810 .with_writer(make_writer.clone())
1811 .with_file(true)
1812 .with_line_number(true)
1813 .with_level(false)
1814 .with_ansi(false)
1815 .with_timer(MockTime);
1816
1817 let expected = Regex::new(&format!(
1818 "^fake time tracing_subscriber::fmt::format::test: {}:[0-9]+: hello\n$",
1819 current_path()
1820 // if we're on Windows, the path might contain backslashes, which
1821 // have to be escpaed before compiling the regex.
1822 .replace('\\', "\\\\")
1823 ))
1824 .unwrap();
1825 let _default = set_default(&subscriber.into());
1826 tracing::info!("hello");
1827 let res = make_writer.get_string();
1828 assert!(expected.is_match(&res));
1829 }
1830
1831 #[test]
1832 fn with_line_number() {
1833 let make_writer = MockMakeWriter::default();
1834 let subscriber = crate::fmt::Subscriber::builder()
1835 .with_writer(make_writer.clone())
1836 .with_line_number(true)
1837 .with_level(false)
1838 .with_ansi(false)
1839 .with_timer(MockTime);
1840
1841 let expected =
1842 Regex::new("^fake time tracing_subscriber::fmt::format::test: [0-9]+: hello\n$")
1843 .unwrap();
1844 let _default = set_default(&subscriber.into());
1845 tracing::info!("hello");
1846 let res = make_writer.get_string();
1847 assert!(expected.is_match(&res));
1848 }
1849
1850 #[test]
1851 fn with_filename() {
1852 let make_writer = MockMakeWriter::default();
1853 let subscriber = crate::fmt::Subscriber::builder()
1854 .with_writer(make_writer.clone())
1855 .with_file(true)
1856 .with_level(false)
1857 .with_ansi(false)
1858 .with_timer(MockTime);
1859 let expected = &format!(
1860 "fake time tracing_subscriber::fmt::format::test: {}: hello\n",
1861 current_path(),
1862 );
1863 assert_info_hello(subscriber, make_writer, expected);
1864 }
1865
1866 #[test]
1867 fn with_thread_ids() {
1868 let make_writer = MockMakeWriter::default();
1869 let subscriber = crate::fmt::Subscriber::builder()
1870 .with_writer(make_writer.clone())
1871 .with_thread_ids(true)
1872 .with_ansi(false)
1873 .with_timer(MockTime);
1874 let expected =
1875 "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n";
1876
1877 assert_info_hello_ignore_numeric(subscriber, make_writer, expected);
1878 }
1879
1880 #[test]
1881 fn pretty_default() {
1882 let make_writer = MockMakeWriter::default();
1883 let subscriber = crate::fmt::Subscriber::builder()
1884 .pretty()
1885 .with_writer(make_writer.clone())
1886 .with_ansi(false)
1887 .with_timer(MockTime);
1888 let expected = format!(
1889 r#" fake time INFO tracing_subscriber::fmt::format::test: hello
1890 at {}:NUMERIC
1891
1892"#,
1893 file!()
1894 );
1895
1896 assert_info_hello_ignore_numeric(subscriber, make_writer, &expected)
1897 }
1898
1899 fn assert_info_hello(subscriber: impl Into<Dispatch>, buf: MockMakeWriter, expected: &str) {
1900 let _default = set_default(&subscriber.into());
1901 tracing::info!("hello");
1902 let result = buf.get_string();
1903
1904 assert_eq!(expected, result)
1905 }
1906
1907 // When numeric characters are used they often form a non-deterministic value as they usually represent things like a thread id or line number.
1908 // This assert method should be used when non-deterministic numeric characters are present.
1909 fn assert_info_hello_ignore_numeric(
1910 subscriber: impl Into<Dispatch>,
1911 buf: MockMakeWriter,
1912 expected: &str,
1913 ) {
1914 let _default = set_default(&subscriber.into());
1915 tracing::info!("hello");
1916
1917 let regex = Regex::new("[0-9]+").unwrap();
1918 let result = buf.get_string();
1919 let result_cleaned = regex.replace_all(&result, "NUMERIC");
1920
1921 assert_eq!(expected, result_cleaned)
1922 }
1923
a2a8927a
XL
1924 fn test_overridden_parents<T>(
1925 expected: &str,
1926 builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>,
1927 ) where
1928 Format<T, MockTime>: FormatEvent<crate::Registry, DefaultFields>,
1929 T: Send + Sync + 'static,
1930 {
1931 let make_writer = MockMakeWriter::default();
5099ac24 1932 let subscriber = builder
a2a8927a
XL
1933 .with_writer(make_writer.clone())
1934 .with_level(false)
1935 .with_ansi(false)
f035d41b
XL
1936 .with_timer(MockTime)
1937 .finish();
1938
5099ac24 1939 with_default(subscriber, || {
a2a8927a
XL
1940 let span1 = tracing::info_span!("span1", span = 1);
1941 let span2 = tracing::info_span!(parent: &span1, "span2", span = 2);
1942 tracing::info!(parent: &span2, "hello");
f035d41b 1943 });
a2a8927a 1944 assert_eq!(expected, make_writer.get_string());
f035d41b
XL
1945 }
1946
a2a8927a
XL
1947 fn test_overridden_parents_in_scope<T>(
1948 expected1: &str,
1949 expected2: &str,
1950 builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>,
1951 ) where
1952 Format<T, MockTime>: FormatEvent<crate::Registry, DefaultFields>,
1953 T: Send + Sync + 'static,
f035d41b 1954 {
a2a8927a
XL
1955 let make_writer = MockMakeWriter::default();
1956 let subscriber = builder
1957 .with_writer(make_writer.clone())
1958 .with_level(false)
1959 .with_ansi(false)
f035d41b
XL
1960 .with_timer(MockTime)
1961 .finish();
1962
1963 with_default(subscriber, || {
a2a8927a
XL
1964 let span1 = tracing::info_span!("span1", span = 1);
1965 let span2 = tracing::info_span!(parent: &span1, "span2", span = 2);
1966 let span3 = tracing::info_span!("span3", span = 3);
1967 let _e3 = span3.enter();
1968
1969 tracing::info!("hello");
1970 assert_eq!(expected1, make_writer.get_string().as_str());
1971
1972 tracing::info!(parent: &span2, "hello");
1973 assert_eq!(expected2, make_writer.get_string().as_str());
f035d41b 1974 });
a2a8927a 1975 }
f035d41b 1976
a2a8927a
XL
1977 fn run_test(subscriber: impl Into<Dispatch>, buf: MockMakeWriter, expected: &str) {
1978 let _default = set_default(&subscriber.into());
1979 tracing::info!("hello");
1980 assert_eq!(expected, buf.get_string())
f035d41b
XL
1981 }
1982
a2a8927a
XL
1983 mod default {
1984 use super::*;
5099ac24
FG
1985
1986 #[test]
1987 fn with_thread_ids() {
1988 let make_writer = MockMakeWriter::default();
1989 let subscriber = crate::fmt::Subscriber::builder()
1990 .with_writer(make_writer.clone())
1991 .with_thread_ids(true)
1992 .with_ansi(false)
1993 .with_timer(MockTime);
1994 let expected =
1995 "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n";
1996
1997 assert_info_hello_ignore_numeric(subscriber, make_writer, expected);
1998 }
1999
a2a8927a
XL
2000 #[cfg(feature = "ansi")]
2001 #[test]
2002 fn with_ansi_true() {
2003 let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[2mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n";
2004 test_ansi(true, expected, crate::fmt::Subscriber::builder());
f035d41b
XL
2005 }
2006
a2a8927a
XL
2007 #[cfg(feature = "ansi")]
2008 #[test]
2009 fn with_ansi_false() {
2010 let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n";
2011 test_ansi(false, expected, crate::fmt::Subscriber::builder());
2012 }
f035d41b 2013
a2a8927a
XL
2014 #[cfg(not(feature = "ansi"))]
2015 #[test]
2016 fn without_ansi() {
2017 let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n";
2018 test_without_ansi(expected, crate::fmt::Subscriber::builder())
2019 }
f035d41b 2020
a2a8927a
XL
2021 #[test]
2022 fn without_level() {
2023 let expected = "fake time tracing_subscriber::fmt::format::test: hello\n";
2024 test_without_level(expected, crate::fmt::Subscriber::builder())
f035d41b
XL
2025 }
2026
a2a8927a
XL
2027 #[test]
2028 fn overridden_parents() {
2029 let expected = "fake time span1{span=1}:span2{span=2}: tracing_subscriber::fmt::format::test: hello\n";
2030 test_overridden_parents(expected, crate::fmt::Subscriber::builder())
2031 }
f035d41b 2032
a2a8927a
XL
2033 #[test]
2034 fn overridden_parents_in_scope() {
2035 test_overridden_parents_in_scope(
2036 "fake time span3{span=3}: tracing_subscriber::fmt::format::test: hello\n",
2037 "fake time span1{span=1}:span2{span=2}: tracing_subscriber::fmt::format::test: hello\n",
2038 crate::fmt::Subscriber::builder(),
2039 )
2040 }
f035d41b
XL
2041 }
2042
a2a8927a
XL
2043 mod compact {
2044 use super::*;
2045
2046 #[cfg(feature = "ansi")]
2047 #[test]
2048 fn with_ansi_true() {
2049 let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[1mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n";
2050 test_ansi(true, expected, crate::fmt::Subscriber::builder().compact())
f035d41b
XL
2051 }
2052
a2a8927a
XL
2053 #[cfg(feature = "ansi")]
2054 #[test]
2055 fn with_ansi_false() {
2056 let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n";
2057 test_ansi(false, expected, crate::fmt::Subscriber::builder().compact());
2058 }
f035d41b 2059
a2a8927a
XL
2060 #[cfg(not(feature = "ansi"))]
2061 #[test]
2062 fn without_ansi() {
2063 let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n";
2064 test_without_ansi(expected, crate::fmt::Subscriber::builder().compact())
2065 }
f035d41b 2066
a2a8927a
XL
2067 #[test]
2068 fn without_level() {
2069 let expected = "fake time tracing_subscriber::fmt::format::test: hello\n";
2070 test_without_level(expected, crate::fmt::Subscriber::builder().compact());
2071 }
f035d41b 2072
a2a8927a
XL
2073 #[test]
2074 fn overridden_parents() {
2075 let expected = "fake time span1:span2: tracing_subscriber::fmt::format::test: hello span=1 span=2\n";
2076 test_overridden_parents(expected, crate::fmt::Subscriber::builder().compact())
2077 }
f035d41b 2078
a2a8927a
XL
2079 #[test]
2080 fn overridden_parents_in_scope() {
2081 test_overridden_parents_in_scope(
2082 "fake time span3: tracing_subscriber::fmt::format::test: hello span=3\n",
2083 "fake time span1:span2: tracing_subscriber::fmt::format::test: hello span=1 span=2\n",
2084 crate::fmt::Subscriber::builder().compact(),
2085 )
2086 }
f035d41b
XL
2087 }
2088
5099ac24
FG
2089 mod pretty {
2090 use super::*;
2091
2092 #[test]
2093 fn pretty_default() {
2094 let make_writer = MockMakeWriter::default();
2095 let subscriber = crate::fmt::Subscriber::builder()
2096 .pretty()
2097 .with_writer(make_writer.clone())
2098 .with_ansi(false)
2099 .with_timer(MockTime);
2100 let expected = format!(
2101 " fake time INFO tracing_subscriber::fmt::format::test: hello\n at {}:NUMERIC\n\n",
2102 file!()
2103 );
2104
2105 assert_info_hello_ignore_numeric(subscriber, make_writer, &expected)
2106 }
2107 }
2108
f035d41b
XL
2109 #[test]
2110 fn format_nanos() {
2111 fn fmt(t: u64) -> String {
2112 TimingDisplay(t).to_string()
2113 }
2114
2115 assert_eq!(fmt(1), "1.00ns");
2116 assert_eq!(fmt(12), "12.0ns");
2117 assert_eq!(fmt(123), "123ns");
2118 assert_eq!(fmt(1234), "1.23µs");
2119 assert_eq!(fmt(12345), "12.3µs");
2120 assert_eq!(fmt(123456), "123µs");
2121 assert_eq!(fmt(1234567), "1.23ms");
2122 assert_eq!(fmt(12345678), "12.3ms");
2123 assert_eq!(fmt(123456789), "123ms");
2124 assert_eq!(fmt(1234567890), "1.23s");
2125 assert_eq!(fmt(12345678901), "12.3s");
2126 assert_eq!(fmt(123456789012), "123s");
2127 assert_eq!(fmt(1234567890123), "1235s");
2128 }
cdc7bbd5
XL
2129
2130 #[test]
2131 fn fmt_span_combinations() {
2132 let f = FmtSpan::NONE;
136023e0
XL
2133 assert!(!f.contains(FmtSpan::NEW));
2134 assert!(!f.contains(FmtSpan::ENTER));
2135 assert!(!f.contains(FmtSpan::EXIT));
2136 assert!(!f.contains(FmtSpan::CLOSE));
cdc7bbd5
XL
2137
2138 let f = FmtSpan::ACTIVE;
136023e0
XL
2139 assert!(!f.contains(FmtSpan::NEW));
2140 assert!(f.contains(FmtSpan::ENTER));
2141 assert!(f.contains(FmtSpan::EXIT));
2142 assert!(!f.contains(FmtSpan::CLOSE));
cdc7bbd5
XL
2143
2144 let f = FmtSpan::FULL;
136023e0
XL
2145 assert!(f.contains(FmtSpan::NEW));
2146 assert!(f.contains(FmtSpan::ENTER));
2147 assert!(f.contains(FmtSpan::EXIT));
2148 assert!(f.contains(FmtSpan::CLOSE));
cdc7bbd5
XL
2149
2150 let f = FmtSpan::NEW | FmtSpan::CLOSE;
136023e0
XL
2151 assert!(f.contains(FmtSpan::NEW));
2152 assert!(!f.contains(FmtSpan::ENTER));
2153 assert!(!f.contains(FmtSpan::EXIT));
2154 assert!(f.contains(FmtSpan::CLOSE));
cdc7bbd5 2155 }
5099ac24
FG
2156
2157 /// Returns the test's module path.
2158 fn current_path() -> String {
2159 Path::new("tracing-subscriber")
2160 .join("src")
2161 .join("fmt")
2162 .join("format")
2163 .join("mod.rs")
2164 .to_str()
2165 .expect("path must not contain invalid unicode")
2166 .to_owned()
2167 }
f035d41b 2168}