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