diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/tracing-subscriber/src/fmt/format/mod.rs (renamed from vendor/tracing-subscriber-0.3.3/src/fmt/format/mod.rs) | 459 |
1 files changed, 411 insertions, 48 deletions
diff --git a/vendor/tracing-subscriber-0.3.3/src/fmt/format/mod.rs b/vendor/tracing-subscriber/src/fmt/format/mod.rs index 9001e102e..b8a482e55 100644 --- a/vendor/tracing-subscriber-0.3.3/src/fmt/format/mod.rs +++ b/vendor/tracing-subscriber/src/fmt/format/mod.rs @@ -1,4 +1,33 @@ //! Formatters for logging `tracing` events. +//! +//! This module provides several formatter implementations, as well as utilities +//! for implementing custom formatters. +//! +//! # Formatters +//! This module provides a number of formatter implementations: +//! +//! * [`Full`]: The default formatter. This emits human-readable, +//! single-line logs for each event that occurs, with the current span context +//! displayed before the formatted representation of the event. See +//! [here](Full#example-output) for sample output. +//! +//! * [`Compact`]: A variant of the default formatter, optimized for +//! short line lengths. Fields from the current span context are appended to +//! the fields of the formatted event, and span names are not shown; the +//! verbosity level is abbreviated to a single character. See +//! [here](Compact#example-output) for sample output. +//! +//! * [`Pretty`]: Emits excessively pretty, multi-line logs, optimized +//! for human readability. This is primarily intended to be used in local +//! development and debugging, or for command-line applications, where +//! automated analysis and compact storage of logs is less of a priority than +//! readability and visual appeal. See [here](Pretty#example-output) +//! for sample output. +//! +//! * [`Json`]: Outputs newline-delimited JSON logs. This is intended +//! for production use with systems where structured logs are consumed as JSON +//! by analysis and viewing tools. The JSON output is not optimized for human +//! readability. See [here](Json#example-output) for sample output. use super::time::{FormatTime, SystemTime}; use crate::{ field::{MakeOutput, MakeVisitor, RecordFields, VisitFmt, VisitOutput}, @@ -17,7 +46,7 @@ use tracing_core::{ use tracing_log::NormalizeEvent; #[cfg(feature = "ansi")] -use ansi_term::{Colour, Style}; +use nu_ansi_term::{Color, Style}; #[cfg(feature = "json")] mod json; @@ -72,7 +101,7 @@ pub use pretty::*; /// does not support ANSI escape codes (such as a log file), and they should /// not be emitted. /// -/// Crates like [`ansi_term`] and [`owo-colors`] can be used to add ANSI +/// Crates like [`nu_ansi_term`] and [`owo-colors`] can be used to add ANSI /// escape codes to formatted output. /// /// * The actual [`Event`] to be formatted. @@ -153,13 +182,14 @@ pub use pretty::*; /// DEBUG yak_shaving::shaver: some-span{field-on-span=foo}: started shaving yak /// ``` /// +/// [`layer::Context`]: crate::layer::Context /// [`fmt::Layer`]: super::Layer /// [`fmt::Subscriber`]: super::Subscriber /// [`Event`]: tracing::Event /// [implements `FormatFields`]: super::FmtContext#impl-FormatFields<'writer> /// [ANSI terminal escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code /// [`Writer::has_ansi_escapes`]: Writer::has_ansi_escapes -/// [`ansi_term`]: https://crates.io/crates/ansi_term +/// [`nu_ansi_term`]: https://crates.io/crates/nu_ansi_term /// [`owo-colors`]: https://crates.io/crates/owo-colors /// [default formatter]: Full pub trait FormatEvent<S, N> @@ -197,8 +227,8 @@ where /// time a span or event with fields is recorded, the subscriber will format /// those fields with its associated `FormatFields` implementation. /// -/// [set of fields]: ../field/trait.RecordFields.html -/// [`FmtSubscriber`]: ../fmt/struct.Subscriber.html +/// [set of fields]: crate::field::RecordFields +/// [`FmtSubscriber`]: super::Subscriber pub trait FormatFields<'writer> { /// Format the provided `fields` to the provided [`Writer`], returning a result. fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result; @@ -251,7 +281,6 @@ pub fn json() -> Format<Json> { /// Returns a [`FormatFields`] implementation that formats fields using the /// provided function or closure. /// -/// [`FormatFields`]: trait.FormatFields.html pub fn debug_fn<F>(f: F) -> FieldFn<F> where F: Fn(&mut Writer<'_>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, @@ -272,6 +301,8 @@ where /// /// Additionally, a `Writer` may expose additional `tracing`-specific /// information to the formatter implementation. +/// +/// [fields]: tracing_core::field pub struct Writer<'writer> { writer: &'writer mut dyn fmt::Write, // TODO(eliza): add ANSI support @@ -281,28 +312,76 @@ pub struct Writer<'writer> { /// A [`FormatFields`] implementation that formats fields by calling a function /// or closure. /// -/// [`FormatFields`]: trait.FormatFields.html #[derive(Debug, Clone)] pub struct FieldFn<F>(F); /// The [visitor] produced by [`FieldFn`]'s [`MakeVisitor`] implementation. /// -/// [visitor]: ../../field/trait.Visit.html -/// [`FieldFn`]: struct.FieldFn.html -/// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html +/// [visitor]: super::super::field::Visit +/// [`MakeVisitor`]: super::super::field::MakeVisitor pub struct FieldFnVisitor<'a, F> { f: F, writer: Writer<'a>, result: fmt::Result, } -/// Marker for `Format` that indicates that the compact log format should be used. +/// Marker for [`Format`] that indicates that the compact log format should be used. +/// +/// The compact format includes fields from all currently entered spans, after +/// the event's fields. Span names are listed in order before fields are +/// displayed. +/// +/// # Example Output /// -/// The compact format only includes the fields from the most recently entered span. +/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-compact +/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s +/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-compact` +/// <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> +/// <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> +/// <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'm gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">="yay!" </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> +/// <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> +/// <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> +/// <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> +/// <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'm gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">="yay!" </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> +/// <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> +/// <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> +/// <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> +/// <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'm gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">="yay!" </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> +/// <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> +/// <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> +/// <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> +/// <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> +/// <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> +/// +/// </pre> #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub struct Compact; -/// Marker for `Format` that indicates that the verbose log format should be used. +/// Marker for [`Format`] that indicates that the default log format should be used. +/// +/// This formatter shows the span context before printing event data. Spans are +/// displayed including their names and fields. /// -/// The full format includes fields from all entered spans. +/// # Example Output +/// +/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt +/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s +/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt` +/// <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> +/// <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> +/// <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'm gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">="yay!"</font> +/// <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> +/// <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> +/// <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> +/// <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'm gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">="yay!"</font> +/// <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> +/// <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> +/// <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> +/// <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'm gonna shave a yak </font><i>excitement</i><font color="#AAAAAA">="yay!"</font> +/// <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> +/// <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> +/// <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> +/// <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> +/// <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> +/// </pre> #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub struct Full; @@ -311,8 +390,11 @@ pub struct Full; /// You will usually want to use this as the `FormatEvent` for a `FmtSubscriber`. /// /// The default logging format, [`Full`] includes all fields in each event and its containing -/// spans. The [`Compact`] logging format includes only the fields from the most-recently-entered -/// span. +/// spans. The [`Compact`] logging format is intended to produce shorter log +/// lines; it displays each event's fields, along with fields from the current +/// span context, but other information is abbreviated. The [`Pretty`] logging +/// format is an extra-verbose, multi-line human-readable logging format +/// intended for use in development. #[derive(Debug, Clone)] pub struct Format<F = Full, T = SystemTime> { format: F, @@ -323,6 +405,8 @@ pub struct Format<F = Full, T = SystemTime> { pub(crate) display_level: bool, pub(crate) display_thread_id: bool, pub(crate) display_thread_name: bool, + pub(crate) display_filename: bool, + pub(crate) display_line_number: bool, } // === impl Writer === @@ -499,6 +583,8 @@ impl Default for Format<Full, SystemTime> { display_level: true, display_thread_id: false, display_thread_name: false, + display_filename: false, + display_line_number: false, } } } @@ -517,6 +603,8 @@ impl<F, T> Format<F, T> { display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, + display_filename: self.display_filename, + display_line_number: self.display_line_number, } } @@ -554,6 +642,8 @@ impl<F, T> Format<F, T> { display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, + display_filename: true, + display_line_number: true, } } @@ -571,8 +661,6 @@ impl<F, T> Format<F, T> { /// /// - [`Format::flatten_event`] can be used to enable flattening event fields into the root /// object. - /// - /// [`Format::flatten_event`]: #method.flatten_event #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn json(self) -> Format<Json, T> { @@ -585,6 +673,8 @@ impl<F, T> Format<F, T> { display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, + display_filename: self.display_filename, + display_line_number: self.display_line_number, } } @@ -612,6 +702,8 @@ impl<F, T> Format<F, T> { display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, + display_filename: self.display_filename, + display_line_number: self.display_line_number, } } @@ -626,6 +718,8 @@ impl<F, T> Format<F, T> { display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, + display_filename: self.display_filename, + display_line_number: self.display_line_number, } } @@ -654,9 +748,9 @@ impl<F, T> Format<F, T> { } /// Sets whether or not the [thread ID] of the current thread is displayed - /// when formatting events + /// when formatting events. /// - /// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html + /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids(self, display_thread_id: bool) -> Format<F, T> { Format { display_thread_id, @@ -665,9 +759,9 @@ impl<F, T> Format<F, T> { } /// Sets whether or not the [name] of the current thread is displayed - /// when formatting events + /// when formatting events. /// - /// [name]: https://doc.rust-lang.org/stable/std/thread/index.html#naming-threads + /// [name]: std::thread#naming-threads pub fn with_thread_names(self, display_thread_name: bool) -> Format<F, T> { Format { display_thread_name, @@ -675,6 +769,38 @@ impl<F, T> Format<F, T> { } } + /// Sets whether or not an event's [source code file path][file] is + /// displayed. + /// + /// [file]: tracing_core::Metadata::file + pub fn with_file(self, display_filename: bool) -> Format<F, T> { + Format { + display_filename, + ..self + } + } + + /// Sets whether or not an event's [source code line number][line] is + /// displayed. + /// + /// [line]: tracing_core::Metadata::line + pub fn with_line_number(self, display_line_number: bool) -> Format<F, T> { + Format { + display_line_number, + ..self + } + } + + /// Sets whether or not the source code location from which an event + /// originated is displayed. + /// + /// This is equivalent to calling [`Format::with_file`] and + /// [`Format::with_line_number`] with the same value. + pub fn with_source_location(self, display_location: bool) -> Self { + self.with_line_number(display_location) + .with_file(display_location) + } + #[inline] fn format_timestamp(&self, writer: &mut Writer<'_>) -> fmt::Result where @@ -692,14 +818,24 @@ impl<F, T> Format<F, T> { if writer.has_ansi_escapes() { let style = Style::new().dimmed(); write!(writer, "{}", style.prefix())?; - self.timer.format_time(writer)?; + + // If getting the timestamp failed, don't bail --- only bail on + // formatting errors. + if self.timer.format_time(writer).is_err() { + writer.write_str("<unknown time>")?; + } + write!(writer, "{} ", style.suffix())?; return Ok(()); } } // Otherwise, just format the timestamp without ANSI formatting. - self.timer.format_time(writer)?; + // If getting the timestamp failed, don't bail --- only bail on + // formatting errors. + if self.timer.format_time(writer).is_err() { + writer.write_str("<unknown time>")?; + } writer.write_char(' ') } } @@ -714,7 +850,7 @@ impl<T> Format<Json, T> { /// ```ignore,json /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate", "message":"some message", "key": "value"} /// ``` - /// See [`Json`](../format/struct.Json.html). + /// See [`Json`][super::format::Json]. #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn flatten_event(mut self, flatten_event: bool) -> Format<Json, T> { @@ -725,7 +861,7 @@ impl<T> Format<Json, T> { /// Sets whether or not the formatter will include the current span in /// formatted events. /// - /// See [`format::Json`](../fmt/format/struct.Json.html) + /// See [`format::Json`][Json] #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn with_current_span(mut self, display_current_span: bool) -> Format<Json, T> { @@ -736,7 +872,7 @@ impl<T> Format<Json, T> { /// Sets whether or not the formatter will include a list (from root to /// leaf) of all currently entered spans in formatted events. /// - /// See [`format::Json`](../fmt/format/struct.Json.html) + /// See [`format::Json`][Json] #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn with_span_list(mut self, display_span_list: bool) -> Format<Json, T> { @@ -840,6 +976,34 @@ where )?; } + let line_number = if self.display_line_number { + meta.line() + } else { + None + }; + + if self.display_filename { + if let Some(filename) = meta.file() { + write!( + writer, + "{}{}{}", + dimmed.paint(filename), + dimmed.paint(":"), + if line_number.is_some() { "" } else { " " } + )?; + } + } + + if let Some(line_number) = line_number { + write!( + writer, + "{}{}:{} ", + dimmed.prefix(), + line_number, + dimmed.suffix() + )?; + } + ctx.format_fields(writer.by_ref(), event)?; writeln!(writer) } @@ -918,23 +1082,49 @@ where }; write!(writer, "{}", fmt_ctx)?; + let bold = writer.bold(); + let dimmed = writer.dimmed(); + + let mut needs_space = false; if self.display_target { - write!( - writer, - "{}{} ", - writer.bold().paint(meta.target()), - writer.dimmed().paint(":") - )?; + write!(writer, "{}{}", bold.paint(meta.target()), dimmed.paint(":"))?; + needs_space = true; + } + + if self.display_filename { + if let Some(filename) = meta.file() { + if self.display_target { + writer.write_char(' ')?; + } + write!(writer, "{}{}", bold.paint(filename), dimmed.paint(":"))?; + needs_space = true; + } + } + + if self.display_line_number { + if let Some(line_number) = meta.line() { + write!( + writer, + "{}{}{}{}", + bold.prefix(), + line_number, + bold.suffix(), + dimmed.paint(":") + )?; + needs_space = true; + } + } + + if needs_space { + writer.write_char(' ')?; } ctx.format_fields(writer.by_ref(), event)?; - let dimmed = writer.dimmed(); for span in ctx .event_scope() .into_iter() - .map(crate::registry::Scope::from_root) - .flatten() + .flat_map(crate::registry::Scope::from_root) { let exts = span.extensions(); if let Some(fields) = exts.get::<FormattedFields<N>>() { @@ -962,7 +1152,6 @@ where /// The default [`FormatFields`] implementation. /// -/// [`FormatFields`]: trait.FormatFields.html #[derive(Debug)] pub struct DefaultFields { // reserve the ability to add fields to this without causing a breaking @@ -984,7 +1173,6 @@ pub struct DefaultVisitor<'a> { impl DefaultFields { /// Returns a new default [`FormatFields`] implementation. /// - /// [`FormatFields`]: trait.FormatFields.html pub fn new() -> Self { Self { _private: () } } @@ -1201,6 +1389,14 @@ impl Style { fn paint(&self, d: impl fmt::Display) -> impl fmt::Display { d } + + fn prefix(&self) -> impl fmt::Display { + "" + } + + fn suffix(&self) -> impl fmt::Display { + "" + } } struct FmtThreadName<'a> { @@ -1288,11 +1484,11 @@ impl<'a> fmt::Display for FmtLevel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.ansi { match *self.level { - Level::TRACE => write!(f, "{}", Colour::Purple.paint(TRACE_STR)), - Level::DEBUG => write!(f, "{}", Colour::Blue.paint(DEBUG_STR)), - Level::INFO => write!(f, "{}", Colour::Green.paint(INFO_STR)), - Level::WARN => write!(f, "{}", Colour::Yellow.paint(WARN_STR)), - Level::ERROR => write!(f, "{}", Colour::Red.paint(ERROR_STR)), + Level::TRACE => write!(f, "{}", Color::Purple.paint(TRACE_STR)), + Level::DEBUG => write!(f, "{}", Color::Blue.paint(DEBUG_STR)), + Level::INFO => write!(f, "{}", Color::Green.paint(INFO_STR)), + Level::WARN => write!(f, "{}", Color::Yellow.paint(WARN_STR)), + Level::ERROR => write!(f, "{}", Color::Red.paint(ERROR_STR)), } } else { match *self.level { @@ -1366,7 +1562,7 @@ impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> { /// Configures what points in the span lifecycle are logged as events. /// -/// See also [`with_span_events`](../struct.SubscriberBuilder.html#method.with_span_events). +/// See also [`with_span_events`](super::SubscriberBuilder.html::with_span_events). #[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] pub struct FmtSpan(u8); @@ -1528,7 +1724,9 @@ pub(super) mod test { }; use super::*; - use std::fmt; + + use regex::Regex; + use std::{fmt, path::Path}; pub(crate) struct MockTime; impl FormatTime for MockTime { @@ -1550,7 +1748,7 @@ pub(super) mod test { .with_thread_names(false); #[cfg(feature = "ansi")] let subscriber = subscriber.with_ansi(false); - run_test(subscriber, make_writer, "hello\n") + assert_info_hello(subscriber, make_writer, "hello\n") } fn test_ansi<T>( @@ -1598,6 +1796,124 @@ pub(super) mod test { run_test(subscriber, make_writer, expected); } + #[test] + fn with_line_number_and_file_name() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_file(true) + .with_line_number(true) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime); + + let expected = Regex::new(&format!( + "^fake time tracing_subscriber::fmt::format::test: {}:[0-9]+: hello\n$", + current_path() + // if we're on Windows, the path might contain backslashes, which + // have to be escpaed before compiling the regex. + .replace('\\', "\\\\") + )) + .unwrap(); + let _default = set_default(&subscriber.into()); + tracing::info!("hello"); + let res = make_writer.get_string(); + assert!(expected.is_match(&res)); + } + + #[test] + fn with_line_number() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_line_number(true) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime); + + let expected = + Regex::new("^fake time tracing_subscriber::fmt::format::test: [0-9]+: hello\n$") + .unwrap(); + let _default = set_default(&subscriber.into()); + tracing::info!("hello"); + let res = make_writer.get_string(); + assert!(expected.is_match(&res)); + } + + #[test] + fn with_filename() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_file(true) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime); + let expected = &format!( + "fake time tracing_subscriber::fmt::format::test: {}: hello\n", + current_path(), + ); + assert_info_hello(subscriber, make_writer, expected); + } + + #[test] + fn with_thread_ids() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_thread_ids(true) + .with_ansi(false) + .with_timer(MockTime); + let expected = + "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n"; + + assert_info_hello_ignore_numeric(subscriber, make_writer, expected); + } + + #[test] + fn pretty_default() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .pretty() + .with_writer(make_writer.clone()) + .with_ansi(false) + .with_timer(MockTime); + let expected = format!( + r#" fake time INFO tracing_subscriber::fmt::format::test: hello + at {}:NUMERIC + +"#, + file!() + ); + + assert_info_hello_ignore_numeric(subscriber, make_writer, &expected) + } + + fn assert_info_hello(subscriber: impl Into<Dispatch>, buf: MockMakeWriter, expected: &str) { + let _default = set_default(&subscriber.into()); + tracing::info!("hello"); + let result = buf.get_string(); + + assert_eq!(expected, result) + } + + // When numeric characters are used they often form a non-deterministic value as they usually represent things like a thread id or line number. + // This assert method should be used when non-deterministic numeric characters are present. + fn assert_info_hello_ignore_numeric( + subscriber: impl Into<Dispatch>, + buf: MockMakeWriter, + expected: &str, + ) { + let _default = set_default(&subscriber.into()); + tracing::info!("hello"); + + let regex = Regex::new("[0-9]+").unwrap(); + let result = buf.get_string(); + let result_cleaned = regex.replace_all(&result, "NUMERIC"); + + assert_eq!(expected, result_cleaned) + } + fn test_overridden_parents<T>( expected: &str, builder: crate::fmt::SubscriberBuilder<DefaultFields, Format<T>>, @@ -1606,14 +1922,14 @@ pub(super) mod test { T: Send + Sync + 'static, { let make_writer = MockMakeWriter::default(); - let collector = builder + let subscriber = builder .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .finish(); - with_default(collector, || { + with_default(subscriber, || { let span1 = tracing::info_span!("span1", span = 1); let span2 = tracing::info_span!(parent: &span1, "span2", span = 2); tracing::info!(parent: &span2, "hello"); @@ -1659,6 +1975,21 @@ pub(super) mod test { mod default { use super::*; + + #[test] + fn with_thread_ids() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_thread_ids(true) + .with_ansi(false) + .with_timer(MockTime); + let expected = + "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n"; + + assert_info_hello_ignore_numeric(subscriber, make_writer, expected); + } + #[cfg(feature = "ansi")] #[test] fn with_ansi_true() { @@ -1748,6 +2079,26 @@ pub(super) mod test { } } + mod pretty { + use super::*; + + #[test] + fn pretty_default() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .pretty() + .with_writer(make_writer.clone()) + .with_ansi(false) + .with_timer(MockTime); + let expected = format!( + " fake time INFO tracing_subscriber::fmt::format::test: hello\n at {}:NUMERIC\n\n", + file!() + ); + + assert_info_hello_ignore_numeric(subscriber, make_writer, &expected) + } + } + #[test] fn format_nanos() { fn fmt(t: u64) -> String { @@ -1795,4 +2146,16 @@ pub(super) mod test { assert!(!f.contains(FmtSpan::EXIT)); assert!(f.contains(FmtSpan::CLOSE)); } + + /// Returns the test's module path. + fn current_path() -> String { + Path::new("tracing-subscriber") + .join("src") + .join("fmt") + .join("format") + .join("mod.rs") + .to_str() + .expect("path must not contain invalid unicode") + .to_owned() + } } |