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 test_tracer::TestTracer;
57
58#[doc(hidden)]
59pub mod __private {
60 pub use super::throttle::*;
61}
62
63mod formatter;
64mod layers;
65mod test_tracer;
66mod throttle;
67
68use tracing::level_filters::LevelFilter;
69use tracing_appender::non_blocking::WorkerGuard;
70use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
71
72/// Tracer for application logging.
73///
74/// Manages the configuration and initialization of logging layers,
75/// including standard output, optional journald, and optional file logging.
76#[derive(Debug, Clone)]
77pub struct RethTracer {
78 stdout: LayerInfo,
79 journald: Option<String>,
80 file: Option<(LayerInfo, FileInfo)>,
81 samply: Option<LayerInfo>,
82 #[cfg(feature = "tracy")]
83 tracy: Option<LayerInfo>,
84}
85
86impl RethTracer {
87 /// Constructs a new `Tracer` with default settings.
88 ///
89 /// Initializes with default stdout layer configuration.
90 /// Journald and file layers are not set by default.
91 pub fn new() -> Self {
92 Self {
93 stdout: LayerInfo::default(),
94 journald: None,
95 file: None,
96 samply: None,
97 #[cfg(feature = "tracy")]
98 tracy: None,
99 }
100 }
101
102 /// Sets a custom configuration for the stdout layer.
103 ///
104 /// # Arguments
105 /// * `config` - The `LayerInfo` to use for the stdout layer.
106 pub fn with_stdout(mut self, config: LayerInfo) -> Self {
107 self.stdout = config;
108 self
109 }
110
111 /// Sets the journald layer filter.
112 ///
113 /// # Arguments
114 /// * `filter` - The `filter` to use for the journald layer.
115 pub fn with_journald(mut self, filter: String) -> Self {
116 self.journald = Some(filter);
117 self
118 }
119
120 /// Sets the file layer configuration and associated file info.
121 ///
122 /// # Arguments
123 /// * `config` - The `LayerInfo` to use for the file layer.
124 /// * `file_info` - The `FileInfo` containing details about the log file.
125 pub fn with_file(mut self, config: LayerInfo, file_info: FileInfo) -> Self {
126 self.file = Some((config, file_info));
127 self
128 }
129
130 /// Sets the samply layer configuration.
131 pub fn with_samply(mut self, config: LayerInfo) -> Self {
132 self.samply = Some(config);
133 self
134 }
135
136 /// Sets the tracy layer configuration.
137 #[cfg(feature = "tracy")]
138 pub fn with_tracy(mut self, config: LayerInfo) -> Self {
139 self.tracy = Some(config);
140 self
141 }
142}
143
144impl Default for RethTracer {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150/// Configuration for a logging layer.
151///
152/// This struct holds configuration parameters for a tracing layer, including
153/// the format, filtering directives, optional coloring, and directive.
154#[derive(Debug, Clone)]
155pub struct LayerInfo {
156 format: LogFormat,
157 default_directive: String,
158 filters: String,
159 color: Option<String>,
160}
161
162impl LayerInfo {
163 /// Constructs a new `LayerInfo`.
164 ///
165 /// # Arguments
166 /// * `format` - Specifies the format for log messages. Possible values are:
167 /// - `LogFormat::Json` for JSON formatting.
168 /// - `LogFormat::LogFmt` for logfmt (key=value) formatting.
169 /// - `LogFormat::Terminal` for human-readable, terminal-friendly formatting.
170 /// * `default_directive` - Directive for filtering log messages.
171 /// * `filters` - Additional filtering parameters as a string.
172 /// * `color` - Optional color configuration for the log messages.
173 pub const fn new(
174 format: LogFormat,
175 default_directive: String,
176 filters: String,
177 color: Option<String>,
178 ) -> Self {
179 Self { format, default_directive, filters, color }
180 }
181}
182
183impl Default for LayerInfo {
184 /// Provides default values for `LayerInfo`.
185 ///
186 /// By default, it uses terminal format, INFO level filter,
187 /// no additional filters, and no color configuration.
188 fn default() -> Self {
189 Self {
190 format: LogFormat::Terminal,
191 default_directive: LevelFilter::INFO.to_string(),
192 filters: String::new(),
193 color: Some("always".to_string()),
194 }
195 }
196}
197
198/// Trait defining a general interface for logging configuration.
199///
200/// The `Tracer` trait provides a standardized way to initialize logging configurations
201/// in an application. Implementations of this trait can specify different logging setups,
202/// such as standard output logging, file logging, journald logging, or custom logging
203/// configurations tailored for specific environments (like testing).
204pub trait Tracer: Sized {
205 /// Initialize the logging configuration.
206 ///
207 /// By default, this method creates a new `Layers` instance and delegates to `init_with_layers`.
208 ///
209 /// # Returns
210 /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used,
211 /// or an `Err` in case of an error during initialization.
212 fn init(self) -> eyre::Result<Option<WorkerGuard>> {
213 self.init_with_layers(Layers::new())
214 }
215 /// Initialize the logging configuration with additional custom layers.
216 ///
217 /// This method allows for more customized setup by accepting pre-configured
218 /// `Layers` which can be further customized before initialization.
219 ///
220 /// # Arguments
221 /// * `layers` - Pre-configured `Layers` instance to use for initialization
222 ///
223 /// # Returns
224 /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used,
225 /// or an `Err` in case of an error during initialization.
226 fn init_with_layers(self, layers: Layers) -> eyre::Result<Option<WorkerGuard>>;
227}
228
229impl Tracer for RethTracer {
230 /// Initializes the logging system based on the configured layers.
231 ///
232 /// This method sets up the global tracing subscriber with the specified
233 /// stdout, journald, and file layers.
234 ///
235 /// The default layer is stdout.
236 ///
237 /// # Returns
238 /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used,
239 /// or an `Err` in case of an error during initialization.
240 fn init_with_layers(self, mut layers: Layers) -> eyre::Result<Option<WorkerGuard>> {
241 layers.stdout(
242 self.stdout.format,
243 self.stdout.default_directive.parse()?,
244 &self.stdout.filters,
245 self.stdout.color,
246 )?;
247
248 if let Some(config) = self.journald {
249 layers.journald(&config)?;
250 }
251
252 let file_guard = if let Some((config, file_info)) = self.file {
253 Some(layers.file(config.format, &config.filters, file_info)?)
254 } else {
255 None
256 };
257
258 if let Some(config) = self.samply {
259 layers.samply(config)?;
260 }
261
262 #[cfg(feature = "tracy")]
263 if let Some(config) = self.tracy {
264 layers.tracy(config)?;
265 }
266
267 // The error is returned if the global default subscriber is already set,
268 // so it's safe to ignore it
269 let _ = tracing_subscriber::registry().with(layers.into_inner()).try_init();
270 Ok(file_guard)
271 }
272}
273
274/// Initializes a tracing subscriber for tests.
275///
276/// The filter is configurable via `RUST_LOG`.
277///
278/// # Note
279///
280/// The subscriber will silently fail if it could not be installed.
281pub fn init_test_tracing() {
282 let _ = TestTracer::default().init();
283}