kutil_cli/log/
tracing.rs

1use {
2    anstream::stderr,
3    std::{fs::*, io::*, path::*},
4    time::{format_description::*, macros::*},
5    tracing::subscriber::*,
6    tracing_subscriber::{filter::*, fmt::time::*, prelude::*, *},
7};
8
9// RFC 3339 with subseconds
10// Or ISO 8601 with fewer subsecond digits
11// See: https://time-rs.github.io/book/api/well-known-format-descriptions.html
12const TIME_FORMAT: &[BorrowedFormatItem<'_>] = format_description!(
13    "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3][offset_hour]:[offset_minute]"
14);
15
16/// Initialize a tracing subscriber.
17///
18/// If `path` is [None](Option::None) will use colorized stderr.
19///
20/// * 0: no tracing subscriber.
21/// * 1: [ERROR](tracing::Level::ERROR)
22/// * 2: [WARN](tracing::Level::WARN)
23/// * 3: [INFO](tracing::Level::INFO)
24/// * 4: [DEBUG](tracing::Level::DEBUG)
25/// * >=5: [TRACE](tracing::Level::TRACE)
26pub fn initialize_tracing(verbosity: u8, path: Option<&PathBuf>) -> Result<()> {
27    if verbosity == 0 {
28        return Ok(());
29    }
30
31    let level = verbosity_to_level(verbosity);
32    let timer = LocalTime::new(TIME_FORMAT);
33
34    let builder = fmt().with_max_level(level).with_timer(timer);
35
36    match path {
37        Some(path) => {
38            let file = OpenOptions::new().write(true).create(true).append(true).open(path)?;
39            builder.with_writer(file).with_ansi(false).init();
40        }
41
42        None => builder.with_writer(stderr).init(),
43    };
44
45    Ok(())
46}
47
48/// Initialize a tracing subscriber for journald.
49///
50/// * 0: no tracing subscriber.
51/// * 1: [ERROR](tracing::Level::ERROR)
52/// * 2: [WARN](tracing::Level::WARN)
53/// * 3: [INFO](tracing::Level::INFO)
54/// * 4: [DEBUG](tracing::Level::DEBUG)
55/// * >=5: [TRACE](tracing::Level::TRACE)
56pub fn initialize_tracing_journald(verbosity: u8) -> Result<()> {
57    if verbosity == 0 {
58        return Ok(());
59    }
60
61    let level = verbosity_to_level(verbosity);
62
63    let layer = tracing_journald::layer()?;
64    let subscriber = registry().with(LevelFilter::from_level(level)).with(layer);
65    set_global_default(subscriber).map_err(Error::other)
66}
67
68fn verbosity_to_level(verbosity: u8) -> tracing::Level {
69    match verbosity {
70        1 => tracing::Level::ERROR,
71        2 => tracing::Level::WARN,
72        3 => tracing::Level::INFO,
73        4 => tracing::Level::DEBUG,
74        _ => tracing::Level::TRACE,
75    }
76}