calib_targets_core/
logger.rs

1//! Minimal logger.
2//!
3//! The logger prints `[elapsed LEVEL] message` to stderr with a simple
4//! elapsed-time prefix. Use `init_with_level` to install it once at startup.
5
6use std::io::Write;
7use std::sync::OnceLock;
8use std::time::Instant;
9
10use log::{LevelFilter, Log, Metadata, Record};
11
12#[cfg(feature = "tracing")]
13use tracing_subscriber::fmt::format::FmtSpan;
14#[cfg(feature = "tracing")]
15use tracing_subscriber::util::SubscriberInitExt;
16#[cfg(feature = "tracing")]
17use tracing_subscriber::{fmt, EnvFilter};
18
19struct SimpleLogger {
20    level: LevelFilter,
21    started: Instant,
22}
23
24impl Log for SimpleLogger {
25    fn enabled(&self, metadata: &Metadata) -> bool {
26        metadata.level() <= self.level
27    }
28
29    fn log(&self, record: &Record) {
30        if !self.enabled(record.metadata()) {
31            return;
32        }
33
34        let elapsed = self.started.elapsed().as_secs_f64();
35        let mut stderr = std::io::stderr();
36        let _ = writeln!(
37            stderr,
38            "[{:7.3}s {:>5}] {}",
39            elapsed,
40            record.level(),
41            record.args()
42        );
43    }
44
45    fn flush(&self) {}
46}
47
48static LOGGER: OnceLock<SimpleLogger> = OnceLock::new();
49
50/// Install the simple logger with the provided level filter.
51///
52/// Calling this more than once is a no-op after the first successful
53/// initialization.
54pub fn init_with_level(level: LevelFilter) -> Result<(), log::SetLoggerError> {
55    if LOGGER.get().is_none() {
56        let logger = LOGGER.get_or_init(|| SimpleLogger {
57            level,
58            started: Instant::now(),
59        });
60        log::set_logger(logger)?;
61        log::set_max_level(level);
62    }
63    Ok(())
64}
65
66#[cfg(feature = "tracing")]
67pub fn init_tracing(json: bool) {
68    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
69    if json {
70        let _ = fmt()
71            .with_env_filter(filter)
72            .with_span_events(FmtSpan::CLOSE)
73            .json()
74            .flatten_event(true)
75            .finish()
76            .try_init();
77    } else {
78        let _ = fmt()
79            .with_env_filter(filter)
80            .with_span_events(FmtSpan::CLOSE)
81            .with_timer(fmt::time::Uptime::default())
82            .finish()
83            .try_init();
84    }
85}