Skip to main content

reth_tracing/
formatter.rs

1use crate::layers::BoxedLayer;
2use clap::ValueEnum;
3use std::{fmt, fmt::Display};
4use tracing_appender::non_blocking::NonBlocking;
5use tracing_subscriber::{layer::Filter, Layer, Registry};
6
7/// Represents the logging format.
8///
9/// This enum defines the supported formats for logging output.
10/// It is used to configure the format layer of a tracing subscriber.
11#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)]
12pub enum LogFormat {
13    /// Represents JSON formatting for logs.
14    /// This format outputs log records as JSON objects,
15    /// making it suitable for structured logging.
16    Json,
17
18    /// Represents logfmt (key=value) formatting for logs.
19    /// This format is concise and human-readable,
20    /// typically used in command-line applications.
21    LogFmt,
22
23    /// Represents terminal-friendly formatting for logs.
24    Terminal,
25}
26
27impl LogFormat {
28    /// Applies the specified logging format to create a new layer.
29    ///
30    /// # Arguments
31    /// * `filter` - A filter layer (either `EnvFilter` or a `reload::Layer` wrapping one).
32    /// * `color` - An optional string that enables or disables ANSI color codes in the logs.
33    /// * `show_target` - Whether to show the target module path in log output.
34    /// * `file_writer` - An optional [`NonBlocking`] writer for directing logs to a file. When
35    ///   `None`, logs are written to stdout.
36    ///
37    /// # Returns
38    /// A `BoxedLayer<Registry>` that can be added to a tracing subscriber.
39    ///
40    /// # Environment
41    /// * `RUST_LOG_THREAD_IDS` - Set to any value other than `0` to include the emitting thread's
42    ///   ID in log output. Thread IDs are omitted when it is unset. JSON and terminal output show
43    ///   the Rust [`std::thread::ThreadId`], while logfmt output shows the OS thread ID.
44    pub fn apply<F>(
45        &self,
46        filter: F,
47        color: Option<String>,
48        show_target: bool,
49        file_writer: Option<NonBlocking>,
50    ) -> BoxedLayer<Registry>
51    where
52        F: Filter<Registry> + Send + Sync + 'static,
53    {
54        let ansi = color.is_some_and(|color| {
55            std::env::var("RUST_LOG_STYLE").map(|val| val != "never").unwrap_or(color != "never")
56        });
57
58        let target = std::env::var("RUST_LOG_TARGET").map(|val| val != "0").unwrap_or(show_target);
59        let thread_ids = std::env::var("RUST_LOG_THREAD_IDS").is_ok_and(|val| val != "0");
60
61        match self {
62            Self::Json => {
63                let layer = tracing_subscriber::fmt::layer()
64                    .json()
65                    .with_ansi(ansi)
66                    .with_target(target)
67                    .with_thread_ids(thread_ids);
68                if let Some(writer) = file_writer {
69                    layer.with_writer(writer).with_filter(filter).boxed()
70                } else {
71                    layer.with_filter(filter).boxed()
72                }
73            }
74            Self::LogFmt => {
75                let layer = tracing_logfmt::builder().with_thread_ids(thread_ids).layer();
76                if let Some(writer) = file_writer {
77                    layer.with_writer(writer).with_filter(filter).boxed()
78                } else {
79                    layer.with_filter(filter).boxed()
80                }
81            }
82            Self::Terminal => {
83                let layer = tracing_subscriber::fmt::layer()
84                    .with_ansi(ansi)
85                    .with_target(target)
86                    .with_thread_ids(thread_ids);
87                if let Some(writer) = file_writer {
88                    layer.with_writer(writer).with_filter(filter).boxed()
89                } else {
90                    layer.with_filter(filter).boxed()
91                }
92            }
93        }
94    }
95}
96
97impl Display for LogFormat {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match self {
100            Self::Json => write!(f, "json"),
101            Self::LogFmt => write!(f, "logfmt"),
102            Self::Terminal => write!(f, "terminal"),
103        }
104    }
105}