]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | //! Formatters for logging `tracing` events. |
2 | use super::time::{self, FormatTime, SystemTime}; | |
3 | use crate::{ | |
4 | field::{MakeOutput, MakeVisitor, RecordFields, VisitFmt, VisitOutput}, | |
5 | fmt::fmt_layer::FmtContext, | |
6 | fmt::fmt_layer::FormattedFields, | |
7 | registry::LookupSpan, | |
8 | }; | |
9 | ||
10 | use std::{ | |
11 | fmt::{self, Write}, | |
12 | iter, | |
13 | }; | |
14 | use tracing_core::{ | |
15 | field::{self, Field, Visit}, | |
16 | span, Event, Level, Subscriber, | |
17 | }; | |
18 | ||
19 | #[cfg(feature = "tracing-log")] | |
20 | use tracing_log::NormalizeEvent; | |
21 | ||
22 | #[cfg(feature = "ansi")] | |
23 | use ansi_term::{Colour, Style}; | |
24 | ||
25 | #[cfg(feature = "json")] | |
26 | mod json; | |
f035d41b XL |
27 | #[cfg(feature = "json")] |
28 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
29 | pub use json::*; | |
30 | ||
5869c6ff XL |
31 | #[cfg(feature = "ansi")] |
32 | mod pretty; | |
33 | #[cfg(feature = "ansi")] | |
34 | #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] | |
35 | pub use pretty::*; | |
36 | ||
37 | use fmt::{Debug, Display}; | |
38 | ||
f035d41b XL |
39 | /// A type that can format a tracing `Event` for a `fmt::Write`. |
40 | /// | |
3dfed10e XL |
41 | /// `FormatEvent` is primarily used in the context of [`fmt::Subscriber`] or [`fmt::Layer`]. Each time an event is |
42 | /// dispatched to [`fmt::Subscriber`] or [`fmt::Layer`], the subscriber or layer forwards it to | |
43 | /// its associated `FormatEvent` to emit a log message. | |
f035d41b XL |
44 | /// |
45 | /// This trait is already implemented for function pointers with the same | |
46 | /// signature as `format_event`. | |
47 | /// | |
3dfed10e XL |
48 | /// [`fmt::Subscriber`]: ../struct.Subscriber.html |
49 | /// [`fmt::Layer`]: ../struct.Layer.html | |
f035d41b XL |
50 | pub trait FormatEvent<S, N> |
51 | where | |
52 | S: Subscriber + for<'a> LookupSpan<'a>, | |
53 | N: for<'a> FormatFields<'a> + 'static, | |
54 | { | |
55 | /// Write a log message for `Event` in `Context` to the given `Write`. | |
56 | fn format_event( | |
57 | &self, | |
58 | ctx: &FmtContext<'_, S, N>, | |
59 | writer: &mut dyn fmt::Write, | |
60 | event: &Event<'_>, | |
61 | ) -> fmt::Result; | |
62 | } | |
63 | ||
64 | impl<S, N> FormatEvent<S, N> | |
65 | for fn(ctx: &FmtContext<'_, S, N>, &mut dyn fmt::Write, &Event<'_>) -> fmt::Result | |
66 | where | |
67 | S: Subscriber + for<'a> LookupSpan<'a>, | |
68 | N: for<'a> FormatFields<'a> + 'static, | |
69 | { | |
70 | fn format_event( | |
71 | &self, | |
72 | ctx: &FmtContext<'_, S, N>, | |
73 | writer: &mut dyn fmt::Write, | |
74 | event: &Event<'_>, | |
75 | ) -> fmt::Result { | |
76 | (*self)(ctx, writer, event) | |
77 | } | |
78 | } | |
79 | /// A type that can format a [set of fields] to a `fmt::Write`. | |
80 | /// | |
81 | /// `FormatFields` is primarily used in the context of [`FmtSubscriber`]. Each | |
82 | /// time a span or event with fields is recorded, the subscriber will format | |
83 | /// those fields with its associated `FormatFields` implementation. | |
84 | /// | |
85 | /// [set of fields]: ../field/trait.RecordFields.html | |
86 | /// [`FmtSubscriber`]: ../fmt/struct.Subscriber.html | |
87 | pub trait FormatFields<'writer> { | |
88 | /// Format the provided `fields` to the provided `writer`, returning a result. | |
89 | fn format_fields<R: RecordFields>( | |
90 | &self, | |
91 | writer: &'writer mut dyn fmt::Write, | |
92 | fields: R, | |
93 | ) -> fmt::Result; | |
94 | ||
95 | /// Record additional field(s) on an existing span. | |
96 | /// | |
97 | /// By default, this appends a space to the current set of fields if it is | |
98 | /// non-empty, and then calls `self.format_fields`. If different behavior is | |
99 | /// required, the default implementation of this method can be overridden. | |
100 | fn add_fields(&self, current: &'writer mut String, fields: &span::Record<'_>) -> fmt::Result { | |
101 | if !current.is_empty() { | |
102 | current.push(' '); | |
103 | } | |
104 | self.format_fields(current, fields) | |
105 | } | |
106 | } | |
107 | ||
108 | /// Returns the default configuration for an [event formatter]. | |
109 | /// | |
110 | /// Methods on the returned event formatter can be used for further | |
111 | /// configuration. For example: | |
112 | /// | |
113 | /// ```rust | |
114 | /// let format = tracing_subscriber::fmt::format() | |
115 | /// .without_time() // Don't include timestamps | |
116 | /// .with_target(false) // Don't include event targets. | |
117 | /// .with_level(false) // Don't include event levels. | |
118 | /// .compact(); // Use a more compact, abbreviated format. | |
119 | /// | |
120 | /// // Use the configured formatter when building a new subscriber. | |
121 | /// tracing_subscriber::fmt() | |
122 | /// .event_format(format) | |
123 | /// .init(); | |
124 | /// ``` | |
125 | pub fn format() -> Format { | |
126 | Format::default() | |
127 | } | |
128 | ||
129 | /// Returns the default configuration for a JSON [event formatter]. | |
130 | #[cfg(feature = "json")] | |
131 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
132 | pub fn json() -> Format<Json> { | |
133 | format().json() | |
134 | } | |
135 | ||
136 | /// Returns a [`FormatFields`] implementation that formats fields using the | |
137 | /// provided function or closure. | |
138 | /// | |
139 | /// [`FormatFields`]: trait.FormatFields.html | |
140 | pub fn debug_fn<F>(f: F) -> FieldFn<F> | |
141 | where | |
142 | F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, | |
143 | { | |
144 | FieldFn(f) | |
145 | } | |
146 | ||
147 | /// A [`FormatFields`] implementation that formats fields by calling a function | |
148 | /// or closure. | |
149 | /// | |
150 | /// [`FormatFields`]: trait.FormatFields.html | |
151 | #[derive(Debug, Clone)] | |
152 | pub struct FieldFn<F>(F); | |
153 | /// The [visitor] produced by [`FieldFn`]'s [`MakeVisitor`] implementation. | |
154 | /// | |
155 | /// [visitor]: ../../field/trait.Visit.html | |
156 | /// [`FieldFn`]: struct.FieldFn.html | |
157 | /// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html | |
158 | pub struct FieldFnVisitor<'a, F> { | |
159 | f: F, | |
160 | writer: &'a mut dyn fmt::Write, | |
161 | result: fmt::Result, | |
162 | } | |
163 | /// Marker for `Format` that indicates that the compact log format should be used. | |
164 | /// | |
165 | /// The compact format only includes the fields from the most recently entered span. | |
166 | #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] | |
167 | pub struct Compact; | |
168 | ||
169 | /// Marker for `Format` that indicates that the verbose log format should be used. | |
170 | /// | |
171 | /// The full format includes fields from all entered spans. | |
172 | #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] | |
173 | pub struct Full; | |
174 | ||
175 | /// A pre-configured event formatter. | |
176 | /// | |
177 | /// You will usually want to use this as the `FormatEvent` for a `FmtSubscriber`. | |
178 | /// | |
179 | /// The default logging format, [`Full`] includes all fields in each event and its containing | |
180 | /// spans. The [`Compact`] logging format includes only the fields from the most-recently-entered | |
181 | /// span. | |
182 | #[derive(Debug, Clone)] | |
183 | pub struct Format<F = Full, T = SystemTime> { | |
184 | format: F, | |
185 | pub(crate) timer: T, | |
186 | pub(crate) ansi: bool, | |
187 | pub(crate) display_target: bool, | |
188 | pub(crate) display_level: bool, | |
3dfed10e XL |
189 | pub(crate) display_thread_id: bool, |
190 | pub(crate) display_thread_name: bool, | |
f035d41b XL |
191 | } |
192 | ||
193 | impl Default for Format<Full, SystemTime> { | |
194 | fn default() -> Self { | |
195 | Format { | |
196 | format: Full, | |
197 | timer: SystemTime, | |
198 | ansi: true, | |
199 | display_target: true, | |
200 | display_level: true, | |
3dfed10e XL |
201 | display_thread_id: false, |
202 | display_thread_name: false, | |
f035d41b XL |
203 | } |
204 | } | |
205 | } | |
206 | ||
207 | impl<F, T> Format<F, T> { | |
208 | /// Use a less verbose output format. | |
209 | /// | |
210 | /// See [`Compact`]. | |
211 | pub fn compact(self) -> Format<Compact, T> { | |
212 | Format { | |
213 | format: Compact, | |
214 | timer: self.timer, | |
215 | ansi: self.ansi, | |
216 | display_target: self.display_target, | |
217 | display_level: self.display_level, | |
3dfed10e XL |
218 | display_thread_id: self.display_thread_id, |
219 | display_thread_name: self.display_thread_name, | |
f035d41b XL |
220 | } |
221 | } | |
222 | ||
5869c6ff XL |
223 | /// Use an excessively pretty, human-readable output format. |
224 | /// | |
225 | /// See [`Pretty`]. | |
226 | /// | |
227 | /// Note that this requires the "ansi" feature to be enabled. | |
228 | #[cfg(feature = "ansi")] | |
229 | #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] | |
230 | pub fn pretty(self) -> Format<Pretty, T> { | |
231 | Format { | |
232 | format: Pretty::default(), | |
233 | timer: self.timer, | |
234 | ansi: self.ansi, | |
235 | display_target: self.display_target, | |
236 | display_level: self.display_level, | |
237 | display_thread_id: self.display_thread_id, | |
238 | display_thread_name: self.display_thread_name, | |
239 | } | |
240 | } | |
241 | ||
f035d41b XL |
242 | /// Use the full JSON format. |
243 | /// | |
244 | /// The full format includes fields from all entered spans. | |
245 | /// | |
246 | /// # Example Output | |
247 | /// | |
248 | /// ```ignore,json | |
249 | /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}} | |
250 | /// ``` | |
251 | /// | |
252 | /// # Options | |
253 | /// | |
254 | /// - [`Format::flatten_event`] can be used to enable flattening event fields into the root | |
255 | /// object. | |
256 | /// | |
257 | /// [`Format::flatten_event`]: #method.flatten_event | |
258 | #[cfg(feature = "json")] | |
259 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
260 | pub fn json(self) -> Format<Json, T> { | |
261 | Format { | |
262 | format: Json::default(), | |
263 | timer: self.timer, | |
264 | ansi: self.ansi, | |
265 | display_target: self.display_target, | |
266 | display_level: self.display_level, | |
3dfed10e XL |
267 | display_thread_id: self.display_thread_id, |
268 | display_thread_name: self.display_thread_name, | |
f035d41b XL |
269 | } |
270 | } | |
271 | ||
272 | /// Use the given [`timer`] for log message timestamps. | |
273 | /// | |
274 | /// See [`time`] for the provided timer implementations. | |
275 | /// | |
276 | /// Note that using the `chrono` feature flag enables the | |
277 | /// additional time formatters [`ChronoUtc`] and [`ChronoLocal`]. | |
278 | /// | |
279 | /// [`time`]: ./time/index.html | |
280 | /// [`timer`]: ./time/trait.FormatTime.html | |
281 | /// [`ChronoUtc`]: ./time/struct.ChronoUtc.html | |
282 | /// [`ChronoLocal`]: ./time/struct.ChronoLocal.html | |
283 | pub fn with_timer<T2>(self, timer: T2) -> Format<F, T2> { | |
284 | Format { | |
285 | format: self.format, | |
286 | timer, | |
287 | ansi: self.ansi, | |
288 | display_target: self.display_target, | |
289 | display_level: self.display_level, | |
3dfed10e XL |
290 | display_thread_id: self.display_thread_id, |
291 | display_thread_name: self.display_thread_name, | |
f035d41b XL |
292 | } |
293 | } | |
294 | ||
295 | /// Do not emit timestamps with log messages. | |
296 | pub fn without_time(self) -> Format<F, ()> { | |
297 | Format { | |
298 | format: self.format, | |
299 | timer: (), | |
300 | ansi: self.ansi, | |
301 | display_target: self.display_target, | |
302 | display_level: self.display_level, | |
3dfed10e XL |
303 | display_thread_id: self.display_thread_id, |
304 | display_thread_name: self.display_thread_name, | |
f035d41b XL |
305 | } |
306 | } | |
307 | ||
308 | /// Enable ANSI terminal colors for formatted output. | |
309 | pub fn with_ansi(self, ansi: bool) -> Format<F, T> { | |
310 | Format { ansi, ..self } | |
311 | } | |
312 | ||
313 | /// Sets whether or not an event's target is displayed. | |
314 | pub fn with_target(self, display_target: bool) -> Format<F, T> { | |
315 | Format { | |
316 | display_target, | |
317 | ..self | |
318 | } | |
319 | } | |
320 | ||
321 | /// Sets whether or not an event's level is displayed. | |
322 | pub fn with_level(self, display_level: bool) -> Format<F, T> { | |
323 | Format { | |
324 | display_level, | |
325 | ..self | |
326 | } | |
327 | } | |
3dfed10e XL |
328 | |
329 | /// Sets whether or not the [thread ID] of the current thread is displayed | |
330 | /// when formatting events | |
331 | /// | |
332 | /// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html | |
333 | pub fn with_thread_ids(self, display_thread_id: bool) -> Format<F, T> { | |
334 | Format { | |
335 | display_thread_id, | |
336 | ..self | |
337 | } | |
338 | } | |
339 | ||
340 | /// Sets whether or not the [name] of the current thread is displayed | |
341 | /// when formatting events | |
342 | /// | |
343 | /// [name]: https://doc.rust-lang.org/stable/std/thread/index.html#naming-threads | |
344 | pub fn with_thread_names(self, display_thread_name: bool) -> Format<F, T> { | |
345 | Format { | |
346 | display_thread_name, | |
347 | ..self | |
348 | } | |
349 | } | |
f035d41b XL |
350 | } |
351 | ||
352 | #[cfg(feature = "json")] | |
353 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
354 | impl<T> Format<Json, T> { | |
355 | /// Use the full JSON format with the event's event fields flattened. | |
356 | /// | |
357 | /// # Example Output | |
358 | /// | |
359 | /// ```ignore,json | |
360 | /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate", "message":"some message", "key": "value"} | |
361 | /// ``` | |
362 | /// See [`Json`](../format/struct.Json.html). | |
363 | #[cfg(feature = "json")] | |
364 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
365 | pub fn flatten_event(mut self, flatten_event: bool) -> Format<Json, T> { | |
366 | self.format.flatten_event(flatten_event); | |
367 | self | |
368 | } | |
3dfed10e XL |
369 | |
370 | /// Sets whether or not the formatter will include the current span in | |
371 | /// formatted events. | |
372 | /// | |
373 | /// See [`format::Json`](../fmt/format/struct.Json.html) | |
374 | #[cfg(feature = "json")] | |
375 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
376 | pub fn with_current_span(mut self, display_current_span: bool) -> Format<Json, T> { | |
377 | self.format.with_current_span(display_current_span); | |
378 | self | |
379 | } | |
380 | ||
381 | /// Sets whether or not the formatter will include a list (from root to | |
382 | /// leaf) of all currently entered spans in formatted events. | |
383 | /// | |
384 | /// See [`format::Json`](../fmt/format/struct.Json.html) | |
385 | #[cfg(feature = "json")] | |
386 | #[cfg_attr(docsrs, doc(cfg(feature = "json")))] | |
387 | pub fn with_span_list(mut self, display_span_list: bool) -> Format<Json, T> { | |
388 | self.format.with_span_list(display_span_list); | |
389 | self | |
390 | } | |
f035d41b XL |
391 | } |
392 | ||
393 | impl<S, N, T> FormatEvent<S, N> for Format<Full, T> | |
394 | where | |
395 | S: Subscriber + for<'a> LookupSpan<'a>, | |
396 | N: for<'a> FormatFields<'a> + 'static, | |
397 | T: FormatTime, | |
398 | { | |
399 | fn format_event( | |
400 | &self, | |
401 | ctx: &FmtContext<'_, S, N>, | |
402 | writer: &mut dyn fmt::Write, | |
403 | event: &Event<'_>, | |
404 | ) -> fmt::Result { | |
405 | #[cfg(feature = "tracing-log")] | |
406 | let normalized_meta = event.normalized_metadata(); | |
407 | #[cfg(feature = "tracing-log")] | |
408 | let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); | |
409 | #[cfg(not(feature = "tracing-log"))] | |
410 | let meta = event.metadata(); | |
411 | #[cfg(feature = "ansi")] | |
412 | time::write(&self.timer, writer, self.ansi)?; | |
413 | #[cfg(not(feature = "ansi"))] | |
414 | time::write(&self.timer, writer)?; | |
415 | ||
416 | if self.display_level { | |
417 | let fmt_level = { | |
418 | #[cfg(feature = "ansi")] | |
419 | { | |
420 | FmtLevel::new(meta.level(), self.ansi) | |
421 | } | |
422 | #[cfg(not(feature = "ansi"))] | |
423 | { | |
424 | FmtLevel::new(meta.level()) | |
425 | } | |
426 | }; | |
427 | write!(writer, "{} ", fmt_level)?; | |
428 | } | |
429 | ||
3dfed10e XL |
430 | if self.display_thread_name { |
431 | let current_thread = std::thread::current(); | |
432 | match current_thread.name() { | |
433 | Some(name) => { | |
434 | write!(writer, "{} ", FmtThreadName::new(name))?; | |
435 | } | |
436 | // fall-back to thread id when name is absent and ids are not enabled | |
437 | None if !self.display_thread_id => { | |
438 | write!(writer, "{:0>2?} ", current_thread.id())?; | |
439 | } | |
440 | _ => {} | |
441 | } | |
442 | } | |
443 | ||
444 | if self.display_thread_id { | |
445 | write!(writer, "{:0>2?} ", std::thread::current().id())?; | |
446 | } | |
447 | ||
f035d41b XL |
448 | let full_ctx = { |
449 | #[cfg(feature = "ansi")] | |
450 | { | |
451 | FullCtx::new(ctx, event.parent(), self.ansi) | |
452 | } | |
453 | #[cfg(not(feature = "ansi"))] | |
454 | { | |
455 | FullCtx::new(ctx, event.parent()) | |
456 | } | |
457 | }; | |
458 | ||
459 | write!(writer, "{}", full_ctx)?; | |
460 | if self.display_target { | |
461 | write!(writer, "{}: ", meta.target())?; | |
462 | } | |
463 | ctx.format_fields(writer, event)?; | |
464 | writeln!(writer) | |
465 | } | |
466 | } | |
467 | ||
468 | impl<S, N, T> FormatEvent<S, N> for Format<Compact, T> | |
469 | where | |
470 | S: Subscriber + for<'a> LookupSpan<'a>, | |
471 | N: for<'a> FormatFields<'a> + 'static, | |
472 | T: FormatTime, | |
473 | { | |
474 | fn format_event( | |
475 | &self, | |
476 | ctx: &FmtContext<'_, S, N>, | |
477 | writer: &mut dyn fmt::Write, | |
478 | event: &Event<'_>, | |
479 | ) -> fmt::Result { | |
480 | #[cfg(feature = "tracing-log")] | |
481 | let normalized_meta = event.normalized_metadata(); | |
482 | #[cfg(feature = "tracing-log")] | |
483 | let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); | |
484 | #[cfg(not(feature = "tracing-log"))] | |
485 | let meta = event.metadata(); | |
486 | #[cfg(feature = "ansi")] | |
487 | time::write(&self.timer, writer, self.ansi)?; | |
488 | #[cfg(not(feature = "ansi"))] | |
489 | time::write(&self.timer, writer)?; | |
490 | ||
491 | if self.display_level { | |
492 | let fmt_level = { | |
493 | #[cfg(feature = "ansi")] | |
494 | { | |
495 | FmtLevel::new(meta.level(), self.ansi) | |
496 | } | |
497 | #[cfg(not(feature = "ansi"))] | |
498 | { | |
499 | FmtLevel::new(meta.level()) | |
500 | } | |
501 | }; | |
502 | write!(writer, "{} ", fmt_level)?; | |
503 | } | |
504 | ||
3dfed10e XL |
505 | if self.display_thread_name { |
506 | let current_thread = std::thread::current(); | |
507 | match current_thread.name() { | |
508 | Some(name) => { | |
509 | write!(writer, "{} ", FmtThreadName::new(name))?; | |
510 | } | |
511 | // fall-back to thread id when name is absent and ids are not enabled | |
512 | None if !self.display_thread_id => { | |
513 | write!(writer, "{:0>2?} ", current_thread.id())?; | |
514 | } | |
515 | _ => {} | |
516 | } | |
517 | } | |
518 | ||
519 | if self.display_thread_id { | |
520 | write!(writer, "{:0>2?} ", std::thread::current().id())?; | |
521 | } | |
522 | ||
f035d41b XL |
523 | let fmt_ctx = { |
524 | #[cfg(feature = "ansi")] | |
525 | { | |
526 | FmtCtx::new(&ctx, event.parent(), self.ansi) | |
527 | } | |
528 | #[cfg(not(feature = "ansi"))] | |
529 | { | |
530 | FmtCtx::new(&ctx, event.parent()) | |
531 | } | |
532 | }; | |
533 | write!(writer, "{}", fmt_ctx)?; | |
534 | if self.display_target { | |
535 | write!(writer, "{}:", meta.target())?; | |
536 | } | |
537 | ctx.format_fields(writer, event)?; | |
538 | let span = ctx.ctx.current_span(); | |
539 | if let Some(id) = span.id() { | |
540 | if let Some(span) = ctx.ctx.metadata(id) { | |
541 | write!(writer, "{}", span.fields()).unwrap_or(()); | |
542 | } | |
543 | } | |
544 | writeln!(writer) | |
545 | } | |
546 | } | |
547 | ||
548 | // === impl FormatFields === | |
5869c6ff | 549 | |
f035d41b XL |
550 | impl<'writer, M> FormatFields<'writer> for M |
551 | where | |
552 | M: MakeOutput<&'writer mut dyn fmt::Write, fmt::Result>, | |
553 | M::Visitor: VisitFmt + VisitOutput<fmt::Result>, | |
554 | { | |
555 | fn format_fields<R: RecordFields>( | |
556 | &self, | |
557 | writer: &'writer mut dyn fmt::Write, | |
558 | fields: R, | |
559 | ) -> fmt::Result { | |
560 | let mut v = self.make_visitor(writer); | |
561 | fields.record(&mut v); | |
562 | v.finish() | |
563 | } | |
564 | } | |
565 | /// The default [`FormatFields`] implementation. | |
566 | /// | |
567 | /// [`FormatFields`]: trait.FormatFields.html | |
568 | #[derive(Debug)] | |
569 | pub struct DefaultFields { | |
570 | // reserve the ability to add fields to this without causing a breaking | |
571 | // change in the future. | |
572 | _private: (), | |
573 | } | |
574 | ||
575 | /// The [visitor] produced by [`DefaultFields`]'s [`MakeVisitor`] implementation. | |
576 | /// | |
577 | /// [visitor]: ../../field/trait.Visit.html | |
578 | /// [`DefaultFields`]: struct.DefaultFields.html | |
579 | /// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html | |
580 | pub struct DefaultVisitor<'a> { | |
581 | writer: &'a mut dyn Write, | |
582 | is_empty: bool, | |
583 | result: fmt::Result, | |
584 | } | |
585 | ||
586 | impl DefaultFields { | |
587 | /// Returns a new default [`FormatFields`] implementation. | |
588 | /// | |
589 | /// [`FormatFields`]: trait.FormatFields.html | |
590 | pub fn new() -> Self { | |
591 | Self { _private: () } | |
592 | } | |
593 | } | |
594 | ||
595 | impl Default for DefaultFields { | |
596 | fn default() -> Self { | |
597 | Self::new() | |
598 | } | |
599 | } | |
600 | ||
601 | impl<'a> MakeVisitor<&'a mut dyn Write> for DefaultFields { | |
602 | type Visitor = DefaultVisitor<'a>; | |
603 | ||
604 | #[inline] | |
605 | fn make_visitor(&self, target: &'a mut dyn Write) -> Self::Visitor { | |
606 | DefaultVisitor::new(target, true) | |
607 | } | |
608 | } | |
609 | ||
610 | // === impl DefaultVisitor === | |
611 | ||
612 | impl<'a> DefaultVisitor<'a> { | |
613 | /// Returns a new default visitor that formats to the provided `writer`. | |
614 | /// | |
615 | /// # Arguments | |
616 | /// - `writer`: the writer to format to. | |
617 | /// - `is_empty`: whether or not any fields have been previously written to | |
618 | /// that writer. | |
619 | pub fn new(writer: &'a mut dyn Write, is_empty: bool) -> Self { | |
620 | Self { | |
621 | writer, | |
622 | is_empty, | |
623 | result: Ok(()), | |
624 | } | |
625 | } | |
626 | ||
627 | fn maybe_pad(&mut self) { | |
628 | if self.is_empty { | |
629 | self.is_empty = false; | |
630 | } else { | |
631 | self.result = write!(self.writer, " "); | |
632 | } | |
633 | } | |
634 | } | |
635 | ||
636 | impl<'a> field::Visit for DefaultVisitor<'a> { | |
637 | fn record_str(&mut self, field: &Field, value: &str) { | |
638 | if self.result.is_err() { | |
639 | return; | |
640 | } | |
641 | ||
642 | if field.name() == "message" { | |
643 | self.record_debug(field, &format_args!("{}", value)) | |
644 | } else { | |
645 | self.record_debug(field, &value) | |
646 | } | |
647 | } | |
648 | ||
649 | fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { | |
650 | if let Some(source) = value.source() { | |
5869c6ff | 651 | self.record_debug(field, &format_args!("{}, {}: {}", value, field, source)) |
f035d41b XL |
652 | } else { |
653 | self.record_debug(field, &format_args!("{}", value)) | |
654 | } | |
655 | } | |
656 | ||
657 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { | |
658 | if self.result.is_err() { | |
659 | return; | |
660 | } | |
661 | ||
662 | self.maybe_pad(); | |
663 | self.result = match field.name() { | |
664 | "message" => write!(self.writer, "{:?}", value), | |
665 | // Skip fields that are actually log metadata that have already been handled | |
666 | #[cfg(feature = "tracing-log")] | |
667 | name if name.starts_with("log.") => Ok(()), | |
668 | name if name.starts_with("r#") => write!(self.writer, "{}={:?}", &name[2..], value), | |
669 | name => write!(self.writer, "{}={:?}", name, value), | |
670 | }; | |
671 | } | |
672 | } | |
673 | ||
674 | impl<'a> crate::field::VisitOutput<fmt::Result> for DefaultVisitor<'a> { | |
675 | fn finish(self) -> fmt::Result { | |
676 | self.result | |
677 | } | |
678 | } | |
679 | ||
680 | impl<'a> crate::field::VisitFmt for DefaultVisitor<'a> { | |
681 | fn writer(&mut self) -> &mut dyn fmt::Write { | |
682 | self.writer | |
683 | } | |
684 | } | |
685 | ||
686 | impl<'a> fmt::Debug for DefaultVisitor<'a> { | |
687 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
688 | f.debug_struct("DefaultVisitor") | |
689 | .field("writer", &format_args!("<dyn fmt::Write>")) | |
690 | .field("is_empty", &self.is_empty) | |
691 | .field("result", &self.result) | |
692 | .finish() | |
693 | } | |
694 | } | |
695 | ||
696 | struct FmtCtx<'a, S, N> { | |
697 | ctx: &'a FmtContext<'a, S, N>, | |
698 | span: Option<&'a span::Id>, | |
699 | #[cfg(feature = "ansi")] | |
700 | ansi: bool, | |
701 | } | |
702 | ||
703 | impl<'a, S, N: 'a> FmtCtx<'a, S, N> | |
704 | where | |
705 | S: Subscriber + for<'lookup> LookupSpan<'lookup>, | |
706 | N: for<'writer> FormatFields<'writer> + 'static, | |
707 | { | |
708 | #[cfg(feature = "ansi")] | |
709 | pub(crate) fn new( | |
710 | ctx: &'a FmtContext<'_, S, N>, | |
711 | span: Option<&'a span::Id>, | |
712 | ansi: bool, | |
713 | ) -> Self { | |
714 | Self { ctx, ansi, span } | |
715 | } | |
716 | ||
717 | #[cfg(not(feature = "ansi"))] | |
718 | pub(crate) fn new(ctx: &'a FmtContext<'_, S, N>, span: Option<&'a span::Id>) -> Self { | |
719 | Self { ctx, span } | |
720 | } | |
721 | ||
722 | fn bold(&self) -> Style { | |
723 | #[cfg(feature = "ansi")] | |
724 | { | |
725 | if self.ansi { | |
726 | return Style::new().bold(); | |
727 | } | |
728 | } | |
729 | ||
730 | Style::new() | |
731 | } | |
732 | } | |
733 | ||
734 | impl<'a, S, N: 'a> fmt::Display for FmtCtx<'a, S, N> | |
735 | where | |
736 | S: Subscriber + for<'lookup> LookupSpan<'lookup>, | |
737 | N: for<'writer> FormatFields<'writer> + 'static, | |
738 | { | |
739 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
740 | let bold = self.bold(); | |
741 | let mut seen = false; | |
742 | ||
743 | let span = self | |
744 | .span | |
745 | .and_then(|id| self.ctx.ctx.span(&id)) | |
746 | .or_else(|| self.ctx.ctx.lookup_current()); | |
747 | ||
748 | let scope = span | |
749 | .into_iter() | |
750 | .flat_map(|span| span.from_root().chain(iter::once(span))); | |
751 | ||
752 | for span in scope { | |
753 | seen = true; | |
754 | write!(f, "{}:", bold.paint(span.metadata().name()))?; | |
755 | } | |
756 | ||
757 | if seen { | |
758 | f.write_char(' ')?; | |
759 | } | |
760 | Ok(()) | |
761 | } | |
762 | } | |
763 | ||
764 | struct FullCtx<'a, S, N> | |
765 | where | |
766 | S: Subscriber + for<'lookup> LookupSpan<'lookup>, | |
767 | N: for<'writer> FormatFields<'writer> + 'static, | |
768 | { | |
769 | ctx: &'a FmtContext<'a, S, N>, | |
770 | span: Option<&'a span::Id>, | |
771 | #[cfg(feature = "ansi")] | |
772 | ansi: bool, | |
773 | } | |
774 | ||
775 | impl<'a, S, N: 'a> FullCtx<'a, S, N> | |
776 | where | |
777 | S: Subscriber + for<'lookup> LookupSpan<'lookup>, | |
778 | N: for<'writer> FormatFields<'writer> + 'static, | |
779 | { | |
780 | #[cfg(feature = "ansi")] | |
781 | pub(crate) fn new( | |
782 | ctx: &'a FmtContext<'a, S, N>, | |
783 | span: Option<&'a span::Id>, | |
784 | ansi: bool, | |
785 | ) -> Self { | |
786 | Self { ctx, span, ansi } | |
787 | } | |
788 | ||
789 | #[cfg(not(feature = "ansi"))] | |
790 | pub(crate) fn new(ctx: &'a FmtContext<'a, S, N>, span: Option<&'a span::Id>) -> Self { | |
791 | Self { ctx, span } | |
792 | } | |
793 | ||
794 | fn bold(&self) -> Style { | |
795 | #[cfg(feature = "ansi")] | |
796 | { | |
797 | if self.ansi { | |
798 | return Style::new().bold(); | |
799 | } | |
800 | } | |
801 | ||
802 | Style::new() | |
803 | } | |
804 | } | |
805 | ||
806 | impl<'a, S, N> fmt::Display for FullCtx<'a, S, N> | |
807 | where | |
808 | S: Subscriber + for<'lookup> LookupSpan<'lookup>, | |
809 | N: for<'writer> FormatFields<'writer> + 'static, | |
810 | { | |
811 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
812 | let bold = self.bold(); | |
813 | let mut seen = false; | |
814 | ||
815 | let span = self | |
816 | .span | |
817 | .and_then(|id| self.ctx.ctx.span(&id)) | |
818 | .or_else(|| self.ctx.ctx.lookup_current()); | |
819 | ||
820 | let scope = span | |
821 | .into_iter() | |
822 | .flat_map(|span| span.from_root().chain(iter::once(span))); | |
823 | ||
824 | for span in scope { | |
825 | write!(f, "{}", bold.paint(span.metadata().name()))?; | |
826 | seen = true; | |
827 | ||
828 | let ext = span.extensions(); | |
829 | let fields = &ext | |
830 | .get::<FormattedFields<N>>() | |
831 | .expect("Unable to find FormattedFields in extensions; this is a bug"); | |
832 | if !fields.is_empty() { | |
833 | write!(f, "{}{}{}", bold.paint("{"), fields, bold.paint("}"))?; | |
834 | } | |
835 | f.write_char(':')?; | |
836 | } | |
837 | ||
838 | if seen { | |
839 | f.write_char(' ')?; | |
840 | } | |
841 | Ok(()) | |
842 | } | |
843 | } | |
844 | ||
845 | #[cfg(not(feature = "ansi"))] | |
846 | struct Style; | |
847 | ||
848 | #[cfg(not(feature = "ansi"))] | |
849 | impl Style { | |
850 | fn new() -> Self { | |
851 | Style | |
852 | } | |
853 | fn paint(&self, d: impl fmt::Display) -> impl fmt::Display { | |
854 | d | |
855 | } | |
856 | } | |
857 | ||
3dfed10e XL |
858 | struct FmtThreadName<'a> { |
859 | name: &'a str, | |
860 | } | |
861 | ||
862 | impl<'a> FmtThreadName<'a> { | |
863 | pub(crate) fn new(name: &'a str) -> Self { | |
864 | Self { name } | |
865 | } | |
866 | } | |
867 | ||
868 | impl<'a> fmt::Display for FmtThreadName<'a> { | |
869 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
870 | use std::sync::atomic::{ | |
871 | AtomicUsize, | |
872 | Ordering::{AcqRel, Acquire, Relaxed}, | |
873 | }; | |
874 | ||
875 | // Track the longest thread name length we've seen so far in an atomic, | |
876 | // so that it can be updated by any thread. | |
877 | static MAX_LEN: AtomicUsize = AtomicUsize::new(0); | |
878 | let len = self.name.len(); | |
879 | // Snapshot the current max thread name length. | |
880 | let mut max_len = MAX_LEN.load(Relaxed); | |
881 | ||
882 | while len > max_len { | |
883 | // Try to set a new max length, if it is still the value we took a | |
884 | // snapshot of. | |
885 | match MAX_LEN.compare_exchange(max_len, len, AcqRel, Acquire) { | |
886 | // We successfully set the new max value | |
887 | Ok(_) => break, | |
888 | // Another thread set a new max value since we last observed | |
889 | // it! It's possible that the new length is actually longer than | |
890 | // ours, so we'll loop again and check whether our length is | |
891 | // still the longest. If not, we'll just use the newer value. | |
892 | Err(actual) => max_len = actual, | |
893 | } | |
894 | } | |
895 | ||
896 | // pad thread name using `max_len` | |
897 | write!(f, "{:>width$}", self.name, width = max_len) | |
898 | } | |
899 | } | |
900 | ||
f035d41b XL |
901 | struct FmtLevel<'a> { |
902 | level: &'a Level, | |
903 | #[cfg(feature = "ansi")] | |
904 | ansi: bool, | |
905 | } | |
906 | ||
907 | impl<'a> FmtLevel<'a> { | |
908 | #[cfg(feature = "ansi")] | |
909 | pub(crate) fn new(level: &'a Level, ansi: bool) -> Self { | |
910 | Self { level, ansi } | |
911 | } | |
912 | ||
913 | #[cfg(not(feature = "ansi"))] | |
914 | pub(crate) fn new(level: &'a Level) -> Self { | |
915 | Self { level } | |
916 | } | |
917 | } | |
918 | ||
919 | const TRACE_STR: &str = "TRACE"; | |
920 | const DEBUG_STR: &str = "DEBUG"; | |
921 | const INFO_STR: &str = " INFO"; | |
922 | const WARN_STR: &str = " WARN"; | |
923 | const ERROR_STR: &str = "ERROR"; | |
924 | ||
925 | #[cfg(not(feature = "ansi"))] | |
926 | impl<'a> fmt::Display for FmtLevel<'a> { | |
927 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
928 | match *self.level { | |
929 | Level::TRACE => f.pad(TRACE_STR), | |
930 | Level::DEBUG => f.pad(DEBUG_STR), | |
931 | Level::INFO => f.pad(INFO_STR), | |
932 | Level::WARN => f.pad(WARN_STR), | |
933 | Level::ERROR => f.pad(ERROR_STR), | |
934 | } | |
935 | } | |
936 | } | |
937 | ||
938 | #[cfg(feature = "ansi")] | |
939 | impl<'a> fmt::Display for FmtLevel<'a> { | |
940 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
941 | if self.ansi { | |
942 | match *self.level { | |
943 | Level::TRACE => write!(f, "{}", Colour::Purple.paint(TRACE_STR)), | |
944 | Level::DEBUG => write!(f, "{}", Colour::Blue.paint(DEBUG_STR)), | |
945 | Level::INFO => write!(f, "{}", Colour::Green.paint(INFO_STR)), | |
946 | Level::WARN => write!(f, "{}", Colour::Yellow.paint(WARN_STR)), | |
947 | Level::ERROR => write!(f, "{}", Colour::Red.paint(ERROR_STR)), | |
948 | } | |
949 | } else { | |
950 | match *self.level { | |
951 | Level::TRACE => f.pad(TRACE_STR), | |
952 | Level::DEBUG => f.pad(DEBUG_STR), | |
953 | Level::INFO => f.pad(INFO_STR), | |
954 | Level::WARN => f.pad(WARN_STR), | |
955 | Level::ERROR => f.pad(ERROR_STR), | |
956 | } | |
957 | } | |
958 | } | |
959 | } | |
960 | ||
961 | // === impl FieldFn === | |
962 | ||
963 | impl<'a, F> MakeVisitor<&'a mut dyn fmt::Write> for FieldFn<F> | |
964 | where | |
965 | F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, | |
966 | { | |
967 | type Visitor = FieldFnVisitor<'a, F>; | |
968 | ||
969 | fn make_visitor(&self, writer: &'a mut dyn fmt::Write) -> Self::Visitor { | |
970 | FieldFnVisitor { | |
971 | writer, | |
972 | f: self.0.clone(), | |
973 | result: Ok(()), | |
974 | } | |
975 | } | |
976 | } | |
977 | ||
978 | impl<'a, F> Visit for FieldFnVisitor<'a, F> | |
979 | where | |
980 | F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result, | |
981 | { | |
982 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { | |
983 | if self.result.is_ok() { | |
984 | self.result = (self.f)(&mut self.writer, field, value) | |
985 | } | |
986 | } | |
987 | } | |
988 | ||
989 | impl<'a, F> VisitOutput<fmt::Result> for FieldFnVisitor<'a, F> | |
990 | where | |
991 | F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result, | |
992 | { | |
993 | fn finish(self) -> fmt::Result { | |
994 | self.result | |
995 | } | |
996 | } | |
997 | ||
998 | impl<'a, F> VisitFmt for FieldFnVisitor<'a, F> | |
999 | where | |
1000 | F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result, | |
1001 | { | |
1002 | fn writer(&mut self) -> &mut dyn fmt::Write { | |
1003 | &mut *self.writer | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> { | |
1008 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1009 | f.debug_struct("FieldFnVisitor") | |
1010 | .field("f", &format_args!("<Fn>")) | |
1011 | .field("writer", &format_args!("<dyn fmt::Write>")) | |
1012 | .field("result", &self.result) | |
1013 | .finish() | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | // === printing synthetic Span events === | |
1018 | ||
1019 | /// Configures what points in the span lifecycle are logged as events. | |
1020 | /// | |
1021 | /// See also [`with_span_events`](../struct.SubscriberBuilder.html#method.with_span_events). | |
1022 | #[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] | |
1023 | pub struct FmtSpan(FmtSpanInner); | |
1024 | ||
1025 | impl FmtSpan { | |
1026 | /// spans are ignored (this is the default) | |
1027 | pub const NONE: FmtSpan = FmtSpan(FmtSpanInner::None); | |
1028 | /// one event per enter/exit of a span | |
1029 | pub const ACTIVE: FmtSpan = FmtSpan(FmtSpanInner::Active); | |
1030 | /// one event when the span is dropped | |
1031 | pub const CLOSE: FmtSpan = FmtSpan(FmtSpanInner::Close); | |
1032 | /// events at all points (new, enter, exit, drop) | |
1033 | pub const FULL: FmtSpan = FmtSpan(FmtSpanInner::Full); | |
1034 | } | |
1035 | ||
1036 | impl Debug for FmtSpan { | |
1037 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1038 | match self.0 { | |
1039 | FmtSpanInner::None => f.write_str("FmtSpan::NONE"), | |
1040 | FmtSpanInner::Active => f.write_str("FmtSpan::ACTIVE"), | |
1041 | FmtSpanInner::Close => f.write_str("FmtSpan::CLOSE"), | |
1042 | FmtSpanInner::Full => f.write_str("FmtSpan::FULL"), | |
1043 | } | |
1044 | } | |
1045 | } | |
1046 | ||
1047 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] | |
1048 | enum FmtSpanInner { | |
1049 | /// spans are ignored (this is the default) | |
1050 | None, | |
1051 | /// one event per enter/exit of a span | |
1052 | Active, | |
1053 | /// one event when the span is dropped | |
1054 | Close, | |
1055 | /// events at all points (new, enter, exit, drop) | |
1056 | Full, | |
1057 | } | |
1058 | ||
1059 | pub(super) struct FmtSpanConfig { | |
1060 | pub(super) kind: FmtSpan, | |
1061 | pub(super) fmt_timing: bool, | |
1062 | } | |
1063 | ||
1064 | impl FmtSpanConfig { | |
1065 | pub(super) fn without_time(self) -> Self { | |
1066 | Self { | |
1067 | kind: self.kind, | |
1068 | fmt_timing: false, | |
1069 | } | |
1070 | } | |
1071 | pub(super) fn with_kind(self, kind: FmtSpan) -> Self { | |
1072 | Self { | |
1073 | kind, | |
1074 | fmt_timing: self.fmt_timing, | |
1075 | } | |
1076 | } | |
1077 | pub(super) fn trace_new(&self) -> bool { | |
29967ef6 | 1078 | matches!(self.kind, FmtSpan::FULL) |
f035d41b XL |
1079 | } |
1080 | pub(super) fn trace_active(&self) -> bool { | |
29967ef6 | 1081 | matches!(self.kind, FmtSpan::ACTIVE | FmtSpan::FULL) |
f035d41b XL |
1082 | } |
1083 | pub(super) fn trace_close(&self) -> bool { | |
29967ef6 | 1084 | matches!(self.kind, FmtSpan::CLOSE | FmtSpan::FULL) |
f035d41b XL |
1085 | } |
1086 | } | |
1087 | ||
1088 | impl Debug for FmtSpanConfig { | |
1089 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1090 | self.kind.fmt(f) | |
1091 | } | |
1092 | } | |
1093 | ||
1094 | impl Default for FmtSpanConfig { | |
1095 | fn default() -> Self { | |
1096 | Self { | |
1097 | kind: FmtSpan::NONE, | |
1098 | fmt_timing: true, | |
1099 | } | |
1100 | } | |
1101 | } | |
1102 | ||
1103 | #[repr(transparent)] | |
1104 | pub(super) struct TimingDisplay(pub(super) u64); | |
1105 | impl Display for TimingDisplay { | |
1106 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1107 | let mut t = self.0 as f64; | |
1108 | for unit in ["ns", "µs", "ms", "s"].iter() { | |
1109 | if t < 10.0 { | |
1110 | return write!(f, "{:.2}{}", t, unit); | |
1111 | } else if t < 100.0 { | |
1112 | return write!(f, "{:.1}{}", t, unit); | |
1113 | } else if t < 1000.0 { | |
1114 | return write!(f, "{:.0}{}", t, unit); | |
1115 | } | |
1116 | t /= 1000.0; | |
1117 | } | |
1118 | write!(f, "{:.0}s", t * 1000.0) | |
1119 | } | |
1120 | } | |
1121 | ||
1122 | #[cfg(test)] | |
1123 | pub(super) mod test { | |
1124 | ||
1125 | use crate::fmt::{test::MockWriter, time::FormatTime}; | |
1126 | use lazy_static::lazy_static; | |
1127 | use tracing::{self, subscriber::with_default}; | |
1128 | ||
1129 | use super::TimingDisplay; | |
1130 | use std::{fmt, sync::Mutex}; | |
1131 | ||
1132 | pub(crate) struct MockTime; | |
1133 | impl FormatTime for MockTime { | |
1134 | fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { | |
1135 | write!(w, "fake time") | |
1136 | } | |
1137 | } | |
1138 | ||
1139 | #[cfg(feature = "ansi")] | |
1140 | #[test] | |
1141 | fn with_ansi_true() { | |
1142 | lazy_static! { | |
1143 | static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]); | |
1144 | } | |
1145 | ||
1146 | let make_writer = || MockWriter::new(&BUF); | |
1147 | let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m tracing_subscriber::fmt::format::test: some ansi test\n"; | |
1148 | test_ansi(make_writer, expected, true, &BUF); | |
1149 | } | |
1150 | ||
1151 | #[cfg(feature = "ansi")] | |
1152 | #[test] | |
1153 | fn with_ansi_false() { | |
1154 | lazy_static! { | |
1155 | static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]); | |
1156 | } | |
1157 | ||
1158 | let make_writer = || MockWriter::new(&BUF); | |
1159 | let expected = "fake time INFO tracing_subscriber::fmt::format::test: some ansi test\n"; | |
1160 | ||
1161 | test_ansi(make_writer, expected, false, &BUF); | |
1162 | } | |
1163 | ||
1164 | #[cfg(not(feature = "ansi"))] | |
1165 | #[test] | |
1166 | fn without_ansi() { | |
1167 | lazy_static! { | |
1168 | static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]); | |
1169 | } | |
1170 | ||
1171 | let make_writer = || MockWriter::new(&BUF); | |
1172 | let expected = "fake time INFO tracing_subscriber::fmt::format::test: some ansi test\n"; | |
1173 | let subscriber = crate::fmt::Subscriber::builder() | |
1174 | .with_writer(make_writer) | |
1175 | .with_timer(MockTime) | |
1176 | .finish(); | |
1177 | ||
1178 | with_default(subscriber, || { | |
1179 | tracing::info!("some ansi test"); | |
1180 | }); | |
1181 | ||
1182 | let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); | |
1183 | assert_eq!(expected, actual.as_str()); | |
1184 | } | |
1185 | ||
1186 | #[cfg(feature = "ansi")] | |
1187 | fn test_ansi<T>(make_writer: T, expected: &str, is_ansi: bool, buf: &Mutex<Vec<u8>>) | |
1188 | where | |
1189 | T: crate::fmt::MakeWriter + Send + Sync + 'static, | |
1190 | { | |
1191 | let subscriber = crate::fmt::Subscriber::builder() | |
1192 | .with_writer(make_writer) | |
1193 | .with_ansi(is_ansi) | |
1194 | .with_timer(MockTime) | |
1195 | .finish(); | |
1196 | ||
1197 | with_default(subscriber, || { | |
1198 | tracing::info!("some ansi test"); | |
1199 | }); | |
1200 | ||
1201 | let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap(); | |
1202 | assert_eq!(expected, actual.as_str()); | |
1203 | } | |
1204 | ||
1205 | #[test] | |
1206 | fn without_level() { | |
1207 | lazy_static! { | |
1208 | static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]); | |
1209 | } | |
1210 | ||
1211 | let make_writer = || MockWriter::new(&BUF); | |
1212 | let subscriber = crate::fmt::Subscriber::builder() | |
1213 | .with_writer(make_writer) | |
1214 | .with_level(false) | |
1215 | .with_ansi(false) | |
1216 | .with_timer(MockTime) | |
1217 | .finish(); | |
1218 | ||
1219 | with_default(subscriber, || { | |
1220 | tracing::info!("hello"); | |
1221 | }); | |
1222 | let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); | |
1223 | assert_eq!( | |
1224 | "fake time tracing_subscriber::fmt::format::test: hello\n", | |
1225 | actual.as_str() | |
1226 | ); | |
1227 | } | |
1228 | ||
1229 | #[test] | |
1230 | fn overridden_parents() { | |
1231 | lazy_static! { | |
1232 | static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]); | |
1233 | } | |
1234 | ||
1235 | let make_writer = || MockWriter::new(&BUF); | |
1236 | let subscriber = crate::fmt::Subscriber::builder() | |
1237 | .with_writer(make_writer) | |
1238 | .with_level(false) | |
1239 | .with_ansi(false) | |
1240 | .with_timer(MockTime) | |
1241 | .finish(); | |
1242 | ||
1243 | with_default(subscriber, || { | |
1244 | let span1 = tracing::info_span!("span1"); | |
1245 | let span2 = tracing::info_span!(parent: &span1, "span2"); | |
1246 | tracing::info!(parent: &span2, "hello"); | |
1247 | }); | |
1248 | let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); | |
1249 | assert_eq!( | |
1250 | "fake time span1:span2: tracing_subscriber::fmt::format::test: hello\n", | |
1251 | actual.as_str() | |
1252 | ); | |
1253 | } | |
1254 | ||
1255 | #[test] | |
1256 | fn overridden_parents_in_scope() { | |
1257 | lazy_static! { | |
1258 | static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]); | |
1259 | } | |
1260 | ||
1261 | let make_writer = || MockWriter::new(&BUF); | |
1262 | let subscriber = crate::fmt::Subscriber::builder() | |
1263 | .with_writer(make_writer) | |
1264 | .with_level(false) | |
1265 | .with_ansi(false) | |
1266 | .with_timer(MockTime) | |
1267 | .finish(); | |
1268 | ||
1269 | let actual = || { | |
1270 | let mut buf = BUF.try_lock().unwrap(); | |
1271 | let val = String::from_utf8(buf.to_vec()).unwrap(); | |
1272 | buf.clear(); | |
1273 | val | |
1274 | }; | |
1275 | ||
1276 | with_default(subscriber, || { | |
1277 | let span1 = tracing::info_span!("span1"); | |
1278 | let span2 = tracing::info_span!(parent: &span1, "span2"); | |
1279 | let span3 = tracing::info_span!("span3"); | |
1280 | let _e3 = span3.enter(); | |
1281 | ||
1282 | tracing::info!("hello"); | |
1283 | assert_eq!( | |
1284 | "fake time span3: tracing_subscriber::fmt::format::test: hello\n", | |
1285 | actual().as_str() | |
1286 | ); | |
1287 | ||
1288 | tracing::info!(parent: &span2, "hello"); | |
1289 | assert_eq!( | |
1290 | "fake time span1:span2: tracing_subscriber::fmt::format::test: hello\n", | |
1291 | actual().as_str() | |
1292 | ); | |
1293 | }); | |
1294 | } | |
1295 | ||
1296 | #[test] | |
1297 | fn format_nanos() { | |
1298 | fn fmt(t: u64) -> String { | |
1299 | TimingDisplay(t).to_string() | |
1300 | } | |
1301 | ||
1302 | assert_eq!(fmt(1), "1.00ns"); | |
1303 | assert_eq!(fmt(12), "12.0ns"); | |
1304 | assert_eq!(fmt(123), "123ns"); | |
1305 | assert_eq!(fmt(1234), "1.23µs"); | |
1306 | assert_eq!(fmt(12345), "12.3µs"); | |
1307 | assert_eq!(fmt(123456), "123µs"); | |
1308 | assert_eq!(fmt(1234567), "1.23ms"); | |
1309 | assert_eq!(fmt(12345678), "12.3ms"); | |
1310 | assert_eq!(fmt(123456789), "123ms"); | |
1311 | assert_eq!(fmt(1234567890), "1.23s"); | |
1312 | assert_eq!(fmt(12345678901), "12.3s"); | |
1313 | assert_eq!(fmt(123456789012), "123s"); | |
1314 | assert_eq!(fmt(1234567890123), "1235s"); | |
1315 | } | |
1316 | } |