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 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}