pmcore/routines/
logger.rs

1use std::time::Instant;
2
3use crate::routines::output::OutputFile;
4use crate::routines::settings::Settings;
5use anyhow::Result;
6use tracing_subscriber::fmt::time::FormatTime;
7use tracing_subscriber::fmt::{self};
8use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
9use tracing_subscriber::registry::Registry;
10use tracing_subscriber::util::SubscriberInitExt;
11use tracing_subscriber::EnvFilter;
12
13/// Setup logging for the library
14///
15/// This function sets up logging for the library. It uses the `tracing` crate, and the `tracing-subscriber` crate for formatting.
16///
17/// The log level is defined in the configuration file, and defaults to `INFO`.
18///
19/// If `log_out` is specifified in the configuration file, a log file is created with the specified name.
20///
21/// If not, the log messages are written to stdout.
22pub(crate) fn setup_log(settings: &mut Settings) -> Result<()> {
23    // If neither `stdout` nor `file` are specified, return without setting the subscriber
24    if !settings.log().stdout && !settings.log().write {
25        return Ok(());
26    }
27
28    // Use the log level defined in configuration file
29    let log_level = settings.log().level.clone();
30    let env_filter = EnvFilter::new(log_level);
31
32    let timestamper = CompactTimestamp {
33        start: Instant::now(),
34    };
35
36    // Define a registry with that level as an environment filter
37    let subscriber = Registry::default().with(env_filter);
38
39    // If we do not want output files, we must create the log in the current directory
40    let outputfile = if !settings.output().write {
41        let cd = std::env::current_dir()?;
42        OutputFile::new(&cd.to_string_lossy(), "log.txt")?
43    } else {
44        OutputFile::new(&settings.output().path, "log.txt")?
45    };
46
47    // Define layer for file
48    let file_layer = match settings.log().write {
49        true => {
50            let layer = fmt::layer()
51                .with_writer(outputfile.file)
52                .with_ansi(false)
53                .with_timer(timestamper.clone());
54
55            Some(layer)
56        }
57        false => None,
58    };
59
60    // Define layer for stdout
61    let stdout_layer = match settings.log().stdout {
62        true => {
63            let layer = fmt::layer()
64                .with_writer(std::io::stdout)
65                .with_ansi(true)
66                .with_target(false)
67                .with_timer(timestamper.clone());
68
69            Some(layer)
70        }
71        false => None,
72    };
73
74    // Combine layers with subscriber
75    subscriber.with(file_layer).with(stdout_layer).init();
76
77    Ok(())
78}
79
80#[derive(Clone)]
81struct CompactTimestamp {
82    start: Instant,
83}
84
85impl FormatTime for CompactTimestamp {
86    fn format_time(
87        &self,
88        w: &mut tracing_subscriber::fmt::format::Writer<'_>,
89    ) -> Result<(), std::fmt::Error> {
90        let elapsed = self.start.elapsed();
91        let hours = elapsed.as_secs() / 3600;
92        let minutes = (elapsed.as_secs() % 3600) / 60;
93        let seconds = elapsed.as_secs() % 60;
94
95        write!(w, "{:02}h {:02}m {:02}s", hours, minutes, seconds)
96    }
97}