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