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}