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