Skip to main content

mountpoint_s3_crt/common/
rust_log_adapter.rs

1//! An adapter between the CRT's [Logger] and the Rust `log` facade
2
3use std::fmt::Write as _;
4
5use smallstr::SmallString;
6
7use crate::common::allocator::Allocator;
8use crate::common::logging::{Level, Logger, LoggerImpl, LoggerInitError, Subject};
9
10/// The log target name for metrics emitted by the CRT
11pub const AWSCRT_LOG_TARGET: &str = "awscrt";
12
13/// This is an implementation of `LoggerImpl` that can be used to pipe CRT log messages into the
14/// Rust `log` facade. To install it, call `RustLogAdapter::try_init()`, and then CRT log messages
15/// will be sent to the `log` facade. These messages will follow that facade's logic for when to
16/// emit log messages. All CRT log messages will have a target that starts with the value of
17/// [AWSCRT_LOG_TARGET].
18#[derive(Debug)]
19#[non_exhaustive]
20pub struct RustLogAdapter;
21
22impl RustLogAdapter {
23    /// Try to install the `log` adapter as the current CRT logger. Only one CRT logger can be
24    /// installed for the lifetime of the program, so this returns Err if a logger has already been
25    /// installed.
26    pub fn try_init() -> Result<(), LoggerInitError> {
27        let logger = Logger::new(&Allocator::default(), Self);
28        logger.try_init()
29    }
30}
31
32impl LoggerImpl for RustLogAdapter {
33    fn log(&self, log_level: Level, subject: Subject, message: &str) {
34        let mut target = SmallString::<[u8; 64]>::new();
35        let _ = write!(target, "{}::{}", AWSCRT_LOG_TARGET, subject.name());
36        log::log!(target: target.as_str(), log_level.into(), "{message}");
37    }
38    fn get_log_level(&self, _subject: Subject) -> Level {
39        log::max_level().to_level().map(|l| l.into()).unwrap_or(Level::None)
40    }
41}
42
43impl From<Level> for log::Level {
44    fn from(level: Level) -> Self {
45        match level {
46            Level::None | Level::Fatal | Level::Error => log::Level::Error,
47            Level::Warn => log::Level::Warn,
48            Level::Info => log::Level::Info,
49            Level::Debug => log::Level::Debug,
50            Level::Trace => log::Level::Trace,
51        }
52    }
53}
54
55impl From<log::Level> for Level {
56    fn from(level: log::Level) -> Self {
57        match level {
58            // Weird special case: the CRT emits some scary looking stuff at `ERROR` level that isn't
59            // actually an error, because it has a separate `FATAL` level for that. `log` doesn't have
60            // `FATAL`. By default most log subscribers will print `ERROR`s, and so would end up
61            // printing those not-really-errors. So let's map `log`'s `ERROR` onto `FATAL` so that we
62            // don't print threatening-looking messages by default.
63            log::Level::Error => Level::Fatal,
64            log::Level::Warn => Level::Warn,
65            log::Level::Info => Level::Info,
66            log::Level::Debug => Level::Debug,
67            log::Level::Trace => Level::Trace,
68        }
69    }
70}