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