reth_tracing/lib.rs
1//! The `tracing` module provides functionalities for setting up and configuring logging.
2//!
3//! It includes structures and functions to create and manage various logging layers: stdout,
4//! file, or journald. The module's primary entry point is the `Tracer` struct, which can be
5//! configured to use different logging formats and destinations. If no layer is specified, it will
6//! default to stdout.
7//!
8//! # Examples
9//!
10//! Basic usage:
11//!
12//! ```
13//! use reth_tracing::{
14//! LayerInfo, RethTracer, Tracer,
15//! tracing::level_filters::LevelFilter,
16//! LogFormat,
17//! };
18//!
19//! fn main() -> eyre::Result<()> {
20//! let tracer = RethTracer::new().with_stdout(LayerInfo::new(
21//! LogFormat::Json,
22//! LevelFilter::INFO.to_string(),
23//! "debug".to_string(),
24//! None,
25//! ));
26//!
27//! tracer.init()?;
28//!
29//! // Your application logic here
30//!
31//! Ok(())
32//! }
33//! ```
34//!
35//! This example sets up a tracer with JSON format logging to stdout.
36
37#![doc(
38 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
39 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
40 issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
41)]
42#![cfg_attr(not(test), warn(unused_crate_dependencies))]
43#![cfg_attr(docsrs, feature(doc_cfg))]
44
45// Re-export tracing crates
46pub use tracing;
47pub use tracing_appender;
48pub use tracing_subscriber;
49
50#[cfg(feature = "tracy")]
51tracy_client::register_demangler!();
52
53// Re-export our types
54pub use formatter::LogFormat;
55pub use layers::{FileInfo, FileWorkerGuard, Layers};
56pub use log_handle::{
57 install_log_handle, log_handle_available, set_log_verbosity, set_log_vmodule,
58 LogFilterReloadHandle,
59};
60pub use test_tracer::TestTracer;
61
62#[doc(hidden)]
63pub mod __private {
64 pub use super::throttle::*;
65}
66
67mod formatter;
68mod layers;
69pub mod log_handle;
70mod test_tracer;
71mod throttle;
72
73use tracing::level_filters::LevelFilter;
74use tracing_appender::non_blocking::WorkerGuard;
75use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
76
77/// Tracer for application logging.
78///
79/// Manages the configuration and initialization of logging layers,
80/// including standard output, optional journald, and optional file logging.
81#[derive(Debug, Clone)]
82pub struct RethTracer {
83 stdout: LayerInfo,
84 journald: Option<String>,
85 file: Option<(LayerInfo, FileInfo)>,
86 samply: Option<LayerInfo>,
87 #[cfg(feature = "tracy")]
88 tracy: Option<LayerInfo>,
89 /// When true, the stdout filter is wrapped in a reload layer so log levels
90 /// can be changed at runtime via RPC (`debug_verbosity`, `debug_vmodule`).
91 enable_reload: bool,
92}
93
94impl RethTracer {
95 /// Constructs a new `Tracer` with default settings.
96 ///
97 /// Initializes with default stdout layer configuration.
98 /// Journald and file layers are not set by default.
99 pub fn new() -> Self {
100 Self {
101 stdout: LayerInfo::default(),
102 journald: None,
103 file: None,
104 samply: None,
105 #[cfg(feature = "tracy")]
106 tracy: None,
107 enable_reload: false,
108 }
109 }
110
111 /// Sets a custom configuration for the stdout layer.
112 ///
113 /// # Arguments
114 /// * `config` - The `LayerInfo` to use for the stdout layer.
115 pub fn with_stdout(mut self, config: LayerInfo) -> Self {
116 self.stdout = config;
117 self
118 }
119
120 /// Sets the journald layer filter.
121 ///
122 /// # Arguments
123 /// * `filter` - The `filter` to use for the journald layer.
124 pub fn with_journald(mut self, filter: String) -> Self {
125 self.journald = Some(filter);
126 self
127 }
128
129 /// Sets the file layer configuration and associated file info.
130 ///
131 /// # Arguments
132 /// * `config` - The `LayerInfo` to use for the file layer.
133 /// * `file_info` - The `FileInfo` containing details about the log file.
134 pub fn with_file(mut self, config: LayerInfo, file_info: FileInfo) -> Self {
135 self.file = Some((config, file_info));
136 self
137 }
138
139 /// Sets the samply layer configuration.
140 pub fn with_samply(mut self, config: LayerInfo) -> Self {
141 self.samply = Some(config);
142 self
143 }
144
145 /// Sets the tracy layer configuration.
146 #[cfg(feature = "tracy")]
147 pub fn with_tracy(mut self, config: LayerInfo) -> Self {
148 self.tracy = Some(config);
149 self
150 }
151
152 /// Enables runtime log filter reloading via RPC (`debug_verbosity`, `debug_vmodule`).
153 pub const fn with_reload(mut self, enable: bool) -> Self {
154 self.enable_reload = enable;
155 self
156 }
157}
158
159impl Default for RethTracer {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165/// Configuration for a logging layer.
166///
167/// This struct holds configuration parameters for a tracing layer, including
168/// the format, filtering directives, optional coloring, and directive.
169#[derive(Debug, Clone)]
170pub struct LayerInfo {
171 format: LogFormat,
172 default_directive: String,
173 filters: String,
174 color: Option<String>,
175}
176
177impl LayerInfo {
178 /// Constructs a new `LayerInfo`.
179 ///
180 /// # Arguments
181 /// * `format` - Specifies the format for log messages. Possible values are:
182 /// - `LogFormat::Json` for JSON formatting.
183 /// - `LogFormat::LogFmt` for logfmt (key=value) formatting.
184 /// - `LogFormat::Terminal` for human-readable, terminal-friendly formatting.
185 /// * `default_directive` - Directive for filtering log messages.
186 /// * `filters` - Additional filtering parameters as a string.
187 /// * `color` - Optional color configuration for the log messages.
188 pub const fn new(
189 format: LogFormat,
190 default_directive: String,
191 filters: String,
192 color: Option<String>,
193 ) -> Self {
194 Self { format, default_directive, filters, color }
195 }
196}
197
198impl Default for LayerInfo {
199 /// Provides default values for `LayerInfo`.
200 ///
201 /// By default, it uses terminal format, INFO level filter,
202 /// no additional filters, and no color configuration.
203 fn default() -> Self {
204 Self {
205 format: LogFormat::Terminal,
206 default_directive: LevelFilter::INFO.to_string(),
207 filters: String::new(),
208 color: Some("always".to_string()),
209 }
210 }
211}
212
213/// Trait defining a general interface for logging configuration.
214///
215/// The `Tracer` trait provides a standardized way to initialize logging configurations
216/// in an application. Implementations of this trait can specify different logging setups,
217/// such as standard output logging, file logging, journald logging, or custom logging
218/// configurations tailored for specific environments (like testing).
219pub trait Tracer: Sized {
220 /// Initialize the logging configuration.
221 ///
222 /// By default, this method creates a new `Layers` instance and delegates to `init_with_layers`.
223 ///
224 /// # Returns
225 /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used,
226 /// or an `Err` in case of an error during initialization.
227 fn init(self) -> eyre::Result<Option<WorkerGuard>> {
228 self.init_with_layers(Layers::new())
229 }
230
231 /// Initialize the logging configuration with additional custom layers.
232 ///
233 /// This is the primary method that implementors must provide.
234 ///
235 /// # Arguments
236 /// * `layers` - Pre-configured `Layers` instance to use for initialization
237 ///
238 /// # Returns
239 /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used,
240 /// or an `Err` in case of an error during initialization.
241 fn init_with_layers(self, layers: Layers) -> eyre::Result<Option<WorkerGuard>>;
242}
243
244impl Tracer for RethTracer {
245 fn init_with_layers(self, mut layers: Layers) -> eyre::Result<Option<WorkerGuard>> {
246 // Configure stdout layer - reloadable if requested for runtime log level changes
247 if let Some(handle) = layers.stdout(
248 self.stdout.format,
249 self.stdout.default_directive.parse()?,
250 &self.stdout.filters,
251 self.stdout.color,
252 self.enable_reload,
253 )? {
254 install_log_handle(handle);
255 }
256
257 if let Some(config) = self.journald {
258 layers.journald(&config)?;
259 }
260
261 let file_guard = if let Some((config, file_info)) = self.file {
262 let (guard, handle) =
263 layers.file(config.format, &config.filters, file_info, self.enable_reload)?;
264 if let Some(handle) = handle {
265 install_log_handle(handle);
266 }
267 Some(guard)
268 } else {
269 None
270 };
271
272 if let Some(config) = self.samply {
273 layers.samply(config)?;
274 }
275
276 #[cfg(feature = "tracy")]
277 if let Some(config) = self.tracy {
278 layers.tracy(config)?;
279 }
280
281 // The error is returned if the global default subscriber is already set,
282 // so it's safe to ignore it
283 let _ = tracing_subscriber::registry().with(layers.into_inner()).try_init();
284
285 Ok(file_guard)
286 }
287}
288
289/// Initializes a tracing subscriber for tests.
290///
291/// The filter is configurable via `RUST_LOG`.
292///
293/// # Note
294///
295/// The subscriber will silently fail if it could not be installed.
296pub fn init_test_tracing() {
297 let _ = TestTracer::default().init();
298}