tracing_print/
lib.rs

1#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
2#![warn(clippy::pedantic)]
3
4use std::fmt;
5use std::marker::PhantomData;
6
7use tracing::field::Visit;
8use tracing::Event;
9use tracing::Level;
10use tracing_subscriber::fmt::format::FormatEvent;
11use tracing_subscriber::fmt::format::FormatFields;
12use tracing_subscriber::fmt::format::Writer;
13use tracing_subscriber::fmt::FmtContext;
14use tracing_subscriber::registry::LookupSpan;
15
16/// Simple println format for `tracing-subscriber`. Prints the message field of
17/// an event and no others.
18///
19/// Formats the message according to log level:
20/// - `ERROR`: red
21/// - `WARN`: yellow
22/// - `INFO`: no formatting
23/// - `DEBUG`: blue
24/// - `TRACE`: dim
25#[derive(Default)]
26pub struct Print {
27    inner: PhantomData<()>,
28}
29
30impl<S, N> FormatEvent<S, N> for Print
31where
32    S: tracing::Subscriber + for<'a> LookupSpan<'a>,
33    N: for<'a> FormatFields<'a> + 'static,
34{
35    fn format_event(
36        &self,
37        _ctx: &FmtContext<'_, S, N>,
38        mut writer: Writer<'_>,
39        event: &Event<'_>,
40    ) -> fmt::Result {
41        let mut visitor = MessageVisitor { message: None };
42        event.record(&mut visitor);
43        let message = visitor.message.ok_or(fmt::Error)?;
44
45        #[cfg(feature = "ansi")]
46        if writer.has_ansi_escapes() {
47            let message = apply_color(*event.metadata().level(), &message);
48            return writeln!(writer, "{message}");
49        }
50
51        writeln!(writer, "{message}")
52    }
53}
54
55#[cfg(feature = "ansi")]
56fn apply_color(level: Level, message: &str) -> yansi::Painted<&str> {
57    use yansi::Paint;
58
59    match level {
60        Level::ERROR => message.red(),
61        Level::WARN => message.yellow(),
62        Level::INFO => yansi::Painted::new(message),
63        Level::DEBUG => message.blue(),
64        Level::TRACE => message.dim(),
65    }
66}
67
68/// A visitor to capture only the `message` field.
69pub struct MessageVisitor {
70    message: Option<String>,
71}
72
73impl Visit for MessageVisitor {
74    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
75        if field.name() == "message" {
76            self.message = Some(value.to_string());
77        }
78    }
79
80    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) {
81        if field.name() == "message" {
82            self.message = Some(format!("{value:?}"));
83        }
84    }
85}