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(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, TracingGuards};
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_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
90
91///  Tracer for application logging.
92///
93///  Manages the configuration and initialization of logging layers,
94/// including standard output, optional journald, and optional file logging.
95#[cfg(feature = "std")]
96#[derive(Debug, Clone)]
97pub struct RethTracer {
98    stdout: LayerInfo,
99    journald: Option<String>,
100    file: Option<(LayerInfo, FileInfo)>,
101    samply: Option<LayerInfo>,
102    chrome: Option<(LayerInfo, std::path::PathBuf)>,
103    #[cfg(feature = "tracy")]
104    tracy: Option<LayerInfo>,
105    /// When true, the stdout filter is wrapped in a reload layer so log levels
106    /// can be changed at runtime.
107    enable_reload: bool,
108}
109
110#[cfg(feature = "std")]
111impl RethTracer {
112    /// Constructs a new `Tracer` with default settings.
113    ///
114    /// Initializes with default stdout layer configuration.
115    /// Journald and file layers are not set by default.
116    pub fn new() -> Self {
117        Self {
118            stdout: LayerInfo::default(),
119            journald: None,
120            file: None,
121            samply: None,
122            chrome: 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 Chrome trace layer configuration.
164    pub fn with_chrome(mut self, config: LayerInfo, file: std::path::PathBuf) -> Self {
165        self.chrome = Some((config, file));
166        self
167    }
168
169    /// Sets the tracy layer configuration.
170    #[cfg(feature = "tracy")]
171    pub fn with_tracy(mut self, config: LayerInfo) -> Self {
172        self.tracy = Some(config);
173        self
174    }
175
176    /// Enables runtime log filter reloading.
177    pub const fn with_reload(mut self, enable: bool) -> Self {
178        self.enable_reload = enable;
179        self
180    }
181}
182
183#[cfg(feature = "std")]
184impl Default for RethTracer {
185    fn default() -> Self {
186        Self::new()
187    }
188}
189
190///  Configuration for a logging layer.
191///
192///  This struct holds configuration parameters for a tracing layer, including
193///  the format, filtering directives, optional coloring, and directive.
194#[cfg(feature = "std")]
195#[derive(Debug, Clone)]
196pub struct LayerInfo {
197    format: LogFormat,
198    default_directive: String,
199    filters: String,
200    color: Option<String>,
201}
202
203#[cfg(feature = "std")]
204impl LayerInfo {
205    ///  Constructs a new `LayerInfo`.
206    ///
207    ///  # Arguments
208    ///  * `format` - Specifies the format for log messages. Possible values are:
209    ///      - `LogFormat::Json` for JSON formatting.
210    ///      - `LogFormat::LogFmt` for logfmt (key=value) formatting.
211    ///      - `LogFormat::Terminal` for human-readable, terminal-friendly formatting.
212    ///  * `default_directive` - Directive for filtering log messages.
213    ///  * `filters` - Additional filtering parameters as a string.
214    ///  * `color` - Optional color configuration for the log messages.
215    pub const fn new(
216        format: LogFormat,
217        default_directive: String,
218        filters: String,
219        color: Option<String>,
220    ) -> Self {
221        Self { format, default_directive, filters, color }
222    }
223}
224
225#[cfg(feature = "std")]
226impl Default for LayerInfo {
227    ///  Provides default values for `LayerInfo`.
228    ///
229    ///  By default, it uses terminal format, INFO level filter,
230    ///  no additional filters, and no color configuration.
231    fn default() -> Self {
232        Self {
233            format: LogFormat::Terminal,
234            default_directive: LevelFilter::INFO.to_string(),
235            filters: String::new(),
236            color: Some("always".to_string()),
237        }
238    }
239}
240
241/// Trait defining a general interface for logging configuration.
242///
243/// The `Tracer` trait provides a standardized way to initialize logging configurations
244/// in an application. Implementations of this trait can specify different logging setups,
245/// such as standard output logging, file logging, journald logging, or custom logging
246/// configurations tailored for specific environments (like testing).
247#[cfg(feature = "std")]
248pub trait Tracer: Sized {
249    /// Initialize the logging configuration.
250    ///
251    /// By default, this method creates a new `Layers` instance and delegates to `init_with_layers`.
252    ///
253    /// # Returns
254    /// An `eyre::Result` with guards for layers that need to stay alive, or an `Err` in case of an
255    /// error during initialization.
256    fn init(self) -> eyre::Result<TracingGuards> {
257        self.init_with_layers(Layers::new())
258    }
259
260    /// Initialize the logging configuration with additional custom layers.
261    ///
262    /// This is the primary method that implementors must provide.
263    ///
264    /// # Arguments
265    /// * `layers` - Pre-configured `Layers` instance to use for initialization
266    ///
267    /// # Returns
268    /// An `eyre::Result` with guards for layers that need to stay alive, or an `Err` in case of an
269    /// error during initialization.
270    fn init_with_layers(self, layers: Layers) -> eyre::Result<TracingGuards>;
271}
272
273#[cfg(feature = "std")]
274impl Tracer for RethTracer {
275    fn init_with_layers(self, mut layers: Layers) -> eyre::Result<TracingGuards> {
276        // Configure stdout layer - reloadable if requested for runtime log level changes
277        if let Some(handle) = layers.stdout(
278            self.stdout.format,
279            self.stdout.default_directive.parse()?,
280            &self.stdout.filters,
281            self.stdout.color,
282            self.enable_reload,
283        )? {
284            install_log_handle(handle);
285        }
286
287        if let Some(config) = self.journald {
288            layers.journald(&config)?;
289        }
290
291        let file_guard = if let Some((config, file_info)) = self.file {
292            let (guard, handle) =
293                layers.file(config.format, &config.filters, file_info, self.enable_reload)?;
294            if let Some(handle) = handle {
295                install_log_handle(handle);
296            }
297            Some(guard)
298        } else {
299            None
300        };
301
302        if let Some(config) = self.samply {
303            layers.samply(config)?;
304        }
305
306        let chrome_guard = if let Some((config, file)) = self.chrome {
307            Some(layers.chrome(config, &file)?)
308        } else {
309            None
310        };
311
312        #[cfg(feature = "tracy")]
313        if let Some(config) = self.tracy {
314            layers.tracy(config)?;
315        }
316
317        // The error is returned if the global default subscriber is already set,
318        // so it's safe to ignore it
319        let _ = tracing_subscriber::registry().with(layers.into_inner()).try_init();
320
321        Ok(TracingGuards::new(file_guard, chrome_guard))
322    }
323}
324
325///  Initializes a tracing subscriber for tests.
326///
327///  The filter is configurable via `RUST_LOG`.
328///
329///  # Note
330///
331///  The subscriber will silently fail if it could not be installed.
332#[cfg(feature = "std")]
333pub fn init_test_tracing() {
334    let _ = TestTracer::default().init();
335}