Skip to main content

tracing_pbar/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(clippy::pedantic)]
3
4#[cfg(feature = "console")]
5pub mod console;
6#[cfg(feature = "lines")]
7pub mod lines;
8#[cfg(feature = "otel")]
9pub mod otel;
10
11use std::fs::OpenOptions;
12
13use anyhow::Result;
14use bon::builder;
15use bon::Builder;
16use camino::Utf8PathBuf;
17use tracing_log::LogTracer;
18use tracing_subscriber::fmt::writer::BoxMakeWriter;
19use tracing_subscriber::layer::SubscriberExt;
20use tracing_subscriber::EnvFilter;
21use tracing_subscriber::Layer;
22use tracing_subscriber::Registry;
23
24/// Type-erased `Layer` for ease of construction.
25pub type BoxLayer = Box<dyn Layer<Registry> + Send + Sync>;
26
27/// All-in-one config for `tracing` layers.
28#[derive(Debug, Clone, Builder)]
29pub struct TracingConfig {
30    #[cfg(feature = "lines")]
31    /// Output file for log lines. Default is **stderr**.
32    #[builder(default = Utf8PathBuf::from("/dev/fd/2"))]
33    log_file: Utf8PathBuf,
34
35    #[cfg(feature = "lines")]
36    /// Filter directive for log lines. Default is **info**.
37    #[builder(default = String::from("info"))]
38    log_level: String,
39
40    #[cfg(feature = "lines")]
41    /// Output format for log lines. Default is **glog**.
42    #[builder(default = lines::LinesFormat::Glog)]
43    log_format: lines::LinesFormat,
44
45    #[cfg(feature = "otel")]
46    /// Service name for OpenTelemetry. Default is **template-rust**.
47    #[builder(default = String::from("template-rust"))]
48    service_name: String,
49}
50
51impl Default for TracingConfig {
52    fn default() -> Self {
53        Self::builder().build()
54    }
55}
56
57impl TracingConfig {
58    /// Initializes a global subscriber and all configured layers.
59    ///
60    /// # Errors
61    ///
62    /// - On failure to setup `tracing_log`
63    /// - (If enabled) On failure opening log lines file
64    /// - (If enabled) On failure to setup OpenTelemetry layer
65    /// - On failure setting global default subscriber
66    pub fn init(&self) -> Result<()> {
67        // TODO: Is this necessary or does tracing-subscriber already do this?
68        LogTracer::init()?;
69
70        // `Registry::with` can take a Vec, easing dynamic construction
71        let mut layers = Vec::new();
72
73        #[cfg(feature = "lines")]
74        {
75            let filter = EnvFilter::builder().parse_lossy(&self.log_level);
76            let writer = OpenOptions::new()
77                .create(true)
78                .append(true)
79                .open(&self.log_file)?;
80            let layer = lines::LinesConfig::builder()
81                .writer(BoxMakeWriter::new(writer))
82                .filter(filter)
83                .format(self.log_format)
84                .build()
85                .layer();
86            layers.push(layer);
87        }
88
89        #[cfg(feature = "otel")]
90        {
91            let layer = otel::OtelConfig::builder()
92                .service_name(self.service_name.to_owned())
93                .build()
94                .layer()?;
95            layers.push(layer);
96        }
97
98        #[cfg(feature = "console")]
99        {
100            let layer = console::ConsoleConfig::builder().build().layer();
101            layers.push(layer);
102        }
103
104        let subscriber = tracing_subscriber::registry().with(layers);
105        tracing::subscriber::set_global_default(subscriber)?;
106
107        Ok(())
108    }
109}