Skip to main content

signstar_common/
logging.rs

1//! Logging utilities.
2
3use log::{LevelFilter, SetLoggerError, set_max_level};
4use simplelog::{ColorChoice, TermLogger, TerminalMode};
5use systemd_journal_logger::{JournalLog, connected_to_journal};
6
7/// Logging setup error.
8#[derive(Debug, thiserror::Error)]
9pub enum Error {
10    /// The process is not connected to the systemd journal.
11    #[error("The process is not connected to the system journal")]
12    JournalNotConnected,
13
14    /// Journal initialization error.
15    #[error("Journal initialization error: {0}")]
16    Journal(std::io::Error),
17
18    /// Logger initialization error.
19    #[error("Logger initialization error: {0}")]
20    Logger(#[from] SetLoggerError),
21}
22
23/// Sets up a global terminal logger based on a maximum logging level filter.
24///
25/// # Errors
26///
27/// Returns an error, if [`TermLogger::init`] fails.
28pub fn setup_terminal_logging(max_level: impl Into<LevelFilter>) -> Result<(), crate::Error> {
29    TermLogger::init(
30        max_level.into(),
31        Default::default(),
32        TerminalMode::Stderr,
33        ColorChoice::Auto,
34    )
35    .map_err(|error| Error::Logger(error).into())
36}
37
38/// Sets up a global systemd journal logger based on a maximum logging level filter.
39///
40/// # Errors
41///
42/// Returns an error, if globally installing a [`JournalLog`] fails.
43pub fn setup_systemd_journal_logging(
44    max_level: impl Into<LevelFilter>,
45) -> Result<(), crate::Error> {
46    JournalLog::new()
47        .map_err(Error::Journal)?
48        .with_extra_fields(vec![("VERSION", env!("CARGO_PKG_VERSION"))])
49        .install()
50        .map_err(Error::Logger)?;
51
52    set_max_level(max_level.into());
53
54    Ok(())
55}
56
57/// Sets up a global systemd journal logger based on a maximum logging level filter, when connected
58/// to the journal.
59///
60/// # Note
61///
62/// Only sets up logging, if the current process is connected to the journal (see `JOURNAL_STREAM`
63/// in [systemd.exec(5)]). This is particularly useful e.g. in [systemd.service(5)] files in which
64/// the executed command is connected to the journal with the help of the `StandardOutput` and
65/// `StandardError` [logging and standard input/output settings].
66///
67/// # Errors
68///
69/// Returns an error, if
70///
71/// - the process is not connected to the systemd journal
72/// - globally installing a [`JournalLog`] fails
73///
74/// [systemd.exec(5)]: https://man.archlinux.org/man/systemd.exec.5
75/// [systemd.service(5)]: https://man.archlinux.org/man/systemd.service.5
76/// [logging and standard input/output settings]: https://man.archlinux.org/man/systemd.exec.5#LOGGING_AND_STANDARD_INPUT/OUTPUT
77pub fn setup_systemd_journal_logging_when_connected(
78    max_level: impl Into<LevelFilter>,
79) -> Result<(), crate::Error> {
80    if !connected_to_journal() {
81        return Err(Error::JournalNotConnected.into());
82    }
83
84    JournalLog::new()
85        .map_err(Error::Journal)?
86        .with_extra_fields(vec![("VERSION", env!("CARGO_PKG_VERSION"))])
87        .install()
88        .map_err(Error::Logger)?;
89
90    set_max_level(max_level.into());
91
92    Ok(())
93}