Skip to main content

studiole_logging/construction/
logger.rs

1//! Logger service that initializes the global tracing subscriber.
2use crate::prelude::*;
3use std::sync::OnceLock;
4use tracing_subscriber::filter::LevelFilter;
5use tracing_subscriber::filter::Targets;
6
7/// Track whether the global tracing subscriber has been installed.
8///
9/// - Guards `tracing::set_global_default`, which succeeds only once per process
10/// - Keeps [`Logger::init`] idempotent across repeated [`ServiceProvider::init`] calls
11static INIT: OnceLock<()> = OnceLock::new();
12
13/// Logger service providing tracing subscriber initialization.
14#[derive(Debug)]
15pub struct Logger {
16    /// Default log level.
17    pub(crate) level: LogLevel,
18    /// Per-target level overrides.
19    pub(crate) targets: Vec<TargetFilter>,
20    /// Optional custom writer for formatted log output.
21    ///
22    /// - When [`Some`], replaces the default `stderr`/`TestWriter` sink
23    /// - Set via [`LoggerBuilder::with_writer`]
24    /// - Default: [`None`]
25    #[cfg(not(target_arch = "wasm32"))]
26    pub(crate) writer: Option<SharedWriter>,
27}
28
29impl Init for Logger {
30    fn init(&self, _services: &ServiceProvider) -> Result<(), Report<InitError>> {
31        let mut output: Result<(), Report<InitError>> = Ok(());
32        INIT.get_or_init(|| {
33            output = self.set();
34        });
35        output
36    }
37}
38
39impl Logger {
40    /// Build the [`Targets`] filter from level and per-target overrides.
41    pub(super) fn build_targets(&self) -> Targets {
42        let mut targets = Targets::new().with_default(LevelFilter::from(self.level));
43        for filter in &self.targets {
44            targets = targets.with_target(&filter.name, LevelFilter::from(filter.level));
45        }
46        targets
47    }
48}