logforth_bridge_log/
lib.rs

1// Copyright 2024 FastLabs Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A bridge to forward logs from the `log` crate to `logforth`.
16
17#![cfg_attr(docsrs, feature(doc_cfg))]
18
19use log::Metadata;
20use log::Record;
21use logforth_core::Logger;
22use logforth_core::default_logger;
23use logforth_core::kv::Key;
24use logforth_core::kv::Value;
25use logforth_core::record::FilterCriteria;
26
27fn level_to_level(level: log::Level) -> logforth_core::record::Level {
28    match level {
29        log::Level::Error => logforth_core::record::Level::Error,
30        log::Level::Warn => logforth_core::record::Level::Warn,
31        log::Level::Info => logforth_core::record::Level::Info,
32        log::Level::Debug => logforth_core::record::Level::Debug,
33        log::Level::Trace => logforth_core::record::Level::Trace,
34    }
35}
36
37struct LogCrateLogger(());
38
39impl log::Log for LogCrateLogger {
40    fn enabled(&self, metadata: &Metadata) -> bool {
41        forward_enabled(default_logger(), metadata)
42    }
43
44    fn log(&self, record: &Record) {
45        forward_log(default_logger(), record);
46    }
47
48    fn flush(&self) {
49        default_logger().flush();
50    }
51}
52
53/// Adapter to use a specific `logforth` logger instance as a `log` crate logger.
54pub struct LogProxy<'a>(&'a Logger);
55
56impl<'a> LogProxy<'a> {
57    /// Create a new `LogProxy` instance.
58    pub fn new(logger: &'a Logger) -> Self {
59        Self(logger)
60    }
61}
62
63impl<'a> log::Log for LogProxy<'a> {
64    fn enabled(&self, metadata: &Metadata) -> bool {
65        forward_enabled(self.0, metadata)
66    }
67
68    fn log(&self, record: &Record) {
69        forward_log(self.0, record);
70    }
71
72    fn flush(&self) {
73        self.0.flush();
74    }
75}
76
77/// Owned version of [`LogProxy`].
78pub struct OwnedLogProxy(Logger);
79
80impl OwnedLogProxy {
81    /// Create a new `OwnedLogProxy` instance.
82    pub fn new(logger: Logger) -> Self {
83        Self(logger)
84    }
85}
86
87impl log::Log for OwnedLogProxy {
88    fn enabled(&self, metadata: &Metadata) -> bool {
89        forward_enabled(&self.0, metadata)
90    }
91
92    fn log(&self, record: &Record) {
93        forward_log(&self.0, record);
94    }
95
96    fn flush(&self) {
97        self.0.flush();
98    }
99}
100
101fn forward_enabled(logger: &Logger, metadata: &Metadata) -> bool {
102    let criteria = FilterCriteria::builder()
103        .target(metadata.target())
104        .level(level_to_level(metadata.level()))
105        .build();
106
107    Logger::enabled(logger, &criteria)
108}
109
110fn forward_log(logger: &Logger, record: &Record) {
111    if !forward_enabled(logger, record.metadata()) {
112        return;
113    }
114
115    // basic fields
116    let mut builder = logforth_core::record::Record::builder()
117        .level(level_to_level(record.level()))
118        .target(record.target())
119        .line(record.line());
120
121    // optional static fields
122    builder = if let Some(module_path) = record.module_path_static() {
123        builder.module_path_static(module_path)
124    } else {
125        builder.module_path(record.module_path())
126    };
127    builder = if let Some(file) = record.file_static() {
128        builder.file_static(file)
129    } else {
130        builder.file(record.file())
131    };
132
133    // payload
134    builder = if let Some(payload) = record.args().as_str() {
135        builder.payload(payload)
136    } else {
137        builder.payload(record.args().to_string())
138    };
139
140    // key-values
141    let mut kvs = Vec::new();
142
143    struct KeyValueVisitor<'a, 'b> {
144        kvs: &'b mut Vec<(log::kv::Key<'a>, log::kv::Value<'a>)>,
145    }
146
147    impl<'a, 'b> log::kv::VisitSource<'a> for KeyValueVisitor<'a, 'b> {
148        fn visit_pair(
149            &mut self,
150            key: log::kv::Key<'a>,
151            value: log::kv::Value<'a>,
152        ) -> Result<(), log::kv::Error> {
153            self.kvs.push((key, value));
154            Ok(())
155        }
156    }
157
158    let mut visitor = KeyValueVisitor { kvs: &mut kvs };
159    record.key_values().visit(&mut visitor).unwrap();
160
161    let mut new_kvs = Vec::with_capacity(kvs.len());
162    for (k, v) in kvs.iter() {
163        new_kvs.push((Key::new_ref(k.as_str()), Value::from_sval2(v)));
164    }
165    builder = builder.key_values(new_kvs.as_slice());
166
167    Logger::log(logger, &builder.build());
168}
169
170/// Set up the log crate global logger.
171///
172/// This function calls [`log::set_logger`] to set up a `LogCrateProxy` and
173/// all logs from log crate will be forwarded to `logforth`'s default logger.
174///
175/// This should be called early in the execution of a Rust program. Any log events that occur
176/// before initialization will be ignored.
177///
178/// This function will set the global maximum log level to `Trace`. To override this, call
179/// [`log::set_max_level`] after this function.
180///
181/// # Errors
182///
183/// Return an error if the log crate global logger has already been set.
184///
185/// # Examples
186///
187/// ```
188/// if let Err(err) = logforth_bridge_log::try_setup() {
189///     eprintln!("failed to setup log crate: {err}");
190/// }
191/// ```
192pub fn try_setup() -> Result<(), log::SetLoggerError> {
193    static LOGGER: LogCrateLogger = LogCrateLogger(());
194    log::set_logger(&LOGGER)?;
195    log::set_max_level(log::LevelFilter::Trace);
196    Ok(())
197}
198
199/// Set up the log crate global logger.
200///
201/// This function calls [`log::set_logger`] to set up a `LogCrateProxy` and
202/// all logs from log crate will be forwarded to `logforth`'s default logger.
203///
204/// This should be called early in the execution of a Rust program. Any log events that occur
205/// before initialization will be ignored.
206///
207/// This function will panic if it is called more than once, or if another library has already
208/// initialized the log crate global logger.
209///
210/// This function will set the global maximum log level to `Trace`. To override this, call
211/// [`log::set_max_level`] after this function.
212///
213/// # Panics
214///
215/// Panic if the log crate global logger has already been set.
216///
217/// # Examples
218///
219/// ```
220/// logforth_bridge_log::setup();
221/// logforth_core::builder().apply()
222/// ```
223pub fn setup() {
224    try_setup().expect(
225        "logforth_bridge_log::setup must be called before the log crate global logger initialized",
226    )
227}