Skip to main content

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}