cat_gateway/
logger.rs

1//! Setup for logging for the service.
2
3use std::sync::OnceLock;
4
5use clap::ValueEnum;
6use tracing::{level_filters::LevelFilter, log::error};
7use tracing_subscriber::{
8    fmt::{self, format::FmtSpan, time},
9    prelude::*,
10    reload::{self, Handle},
11    Registry,
12};
13
14use crate::settings::Settings;
15/// Default log level
16pub(crate) const LOG_LEVEL_DEFAULT: &str = "info";
17
18/// All valid logging levels
19#[derive(ValueEnum, Clone, Copy, Debug)]
20pub(crate) enum LogLevel {
21    /// Debug messages
22    Debug,
23    /// Informational Messages
24    Info,
25    /// Warnings
26    Warn,
27    /// Errors
28    Error,
29}
30
31impl From<LogLevel> for tracing::Level {
32    fn from(val: LogLevel) -> Self {
33        match val {
34            LogLevel::Debug => Self::DEBUG,
35            LogLevel::Info => Self::INFO,
36            LogLevel::Warn => Self::WARN,
37            LogLevel::Error => Self::ERROR,
38        }
39    }
40}
41
42impl From<LogLevel> for tracing::log::LevelFilter {
43    fn from(val: LogLevel) -> Self {
44        match val {
45            LogLevel::Debug => Self::Debug,
46            LogLevel::Info => Self::Info,
47            LogLevel::Warn => Self::Warn,
48            LogLevel::Error => Self::Error,
49        }
50    }
51}
52
53/// Logger Handle for the Service.
54static LOGGER_HANDLE: OnceLock<LoggerHandle> = OnceLock::new();
55
56/// Default Span Guard for the Service.
57static GLOBAL_SPAN: OnceLock<tracing::span::Span> = OnceLock::new();
58
59/// Default Span Guard for the Service.
60static SPAN_GUARD: OnceLock<tracing::span::Entered> = OnceLock::new();
61
62/// Handle to our Logger
63pub(crate) type LoggerHandle = Handle<LevelFilter, Registry>;
64
65/// Set the default fields in a log, using a global span.
66fn set_default_span() {
67    let server_id = Settings::service_id();
68    // This is a hacky way to add fields to every log line.
69    // Add Fields here, as required.
70    let global_span = tracing::info_span!("Global", ServerID = server_id);
71    if GLOBAL_SPAN.set(global_span).is_err() {
72        error!("Failed to set default span.  Is it already set?");
73    }
74
75    // It MUST be Some because of the above.
76    if let Some(global_span) = GLOBAL_SPAN.get() {
77        let span_guard = global_span.enter();
78        if SPAN_GUARD.set(span_guard).is_err() {
79            error!("Failed to set default span.  Is it already set?");
80        }
81    }
82}
83
84/// Initialize the tracing subscriber
85pub(crate) fn init(log_level: LogLevel) {
86    // Create the formatting layer
87    let layer = fmt::layer()
88        .json()
89        .with_timer(time::UtcTime::rfc_3339())
90        .with_span_events(FmtSpan::CLOSE)
91        .with_current_span(true)
92        .with_span_list(true)
93        .with_target(true)
94        .with_file(true)
95        .with_line_number(true)
96        .with_level(true)
97        .with_thread_names(true)
98        .with_thread_ids(true)
99        .flatten_event(true);
100
101    // Create a reloadable layer with the specified log_level
102    let filter = LevelFilter::from_level(log_level.into());
103    let (filter, logger_handle) = reload::Layer::new(filter);
104    tracing_subscriber::registry()
105        .with(filter)
106        .with(layer)
107        .with(
108            tracing_subscriber::EnvFilter::builder()
109                .with_default_directive(LevelFilter::INFO.into())
110                .from_env_lossy(),
111        )
112        .init();
113
114    // Logging is globally disabled by default, so globally enable it to the required level.
115    tracing::log::set_max_level(log_level.into());
116
117    if LOGGER_HANDLE.set(logger_handle).is_err() {
118        error!("Failed to initialize logger handle. Called multiple times?");
119    }
120
121    set_default_span();
122}
123
124/// Modify the logger level setting.
125/// This will reload the logger.
126pub(crate) fn modify_logger_level(level: LogLevel) {
127    if let Some(logger_handle) = LOGGER_HANDLE.get() {
128        if let Err(error) = logger_handle.modify(|f| *f = LevelFilter::from_level(level.into())) {
129            error!("Failed to modify log level to {:?} : {}", level, error);
130        }
131    } else {
132        // This should never happen.
133        error!(
134            "Failed to modify log level to {:?} : Logger handle not available.",
135            level
136        );
137    }
138}