1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //! Trusty simple logger backend
18 //!
19 //! Logs to stderr based on a compile-time configured log level.
20
21 use log::{Level, Log, Metadata, Record};
22 use std::io::{stderr, Write};
23 use std::sync::OnceLock;
24
25 /// Closure type that can be used by external callers to write a custom log formatter
26 ///
27 /// # Examples
28 ///
29 /// ```
30 /// fn log_function(record: &log::Record) -> String {
31 /// let line = match record.line() {
32 /// Some(line) => line,
33 /// None => 0,
34 /// };
35 /// let file = match record.file() {
36 /// Some(file) => file,
37 /// None => "unknown file",
38 /// };
39 /// format!("{}: MyApp - {}:{} {}\n", record.level(), file, line, record.args())
40 /// }
41 /// ```
42 type FormatFn = Box<dyn Fn(&log::Record) -> String + Sync + Send>;
43
44 /// Structure used to modify the logger behavior. It is based on the Android logger configuration
45 /// and implements a subset of its functionality.
46 /// It can be used to override the maximum logging level and to provide a custom log formatting
47 /// function.
48 /// Its default values are Level::Info for its log level and None for its formatter.
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// let config = trusty_log::TrustyLoggerConfig::default()
54 /// .with_min_level(log::Level::Trace)
55 /// .format(&log_function);
56 /// ```
57 pub struct TrustyLoggerConfig {
58 log_level: log::Level,
59 custom_format: Option<FormatFn>,
60 }
61
62 impl TrustyLoggerConfig {
new() -> Self63 pub const fn new() -> Self {
64 TrustyLoggerConfig { log_level: Level::Info, custom_format: None }
65 }
66
with_min_level(mut self, level: log::Level) -> Self67 pub fn with_min_level(mut self, level: log::Level) -> Self {
68 self.log_level = level;
69 self
70 }
71
format<F>(mut self, format: F) -> Self where F: Fn(&log::Record) -> String + Sync + Send + 'static,72 pub fn format<F>(mut self, format: F) -> Self
73 where
74 F: Fn(&log::Record) -> String + Sync + Send + 'static,
75 {
76 self.custom_format = Some(Box::new(format));
77 self
78 }
79 }
80
81 impl Default for TrustyLoggerConfig {
default() -> Self82 fn default() -> Self {
83 TrustyLoggerConfig::new()
84 }
85 }
86
87 /// Main structure used by the logger operations.
88 /// The default values for its config are Level::Info for the log level and None for the formatter.
89 pub struct TrustyLogger {
90 config: TrustyLoggerConfig,
91 }
92
93 impl TrustyLogger {
new(config: TrustyLoggerConfig) -> Self94 pub const fn new(config: TrustyLoggerConfig) -> Self {
95 TrustyLogger { config }
96 }
97 }
98
99 impl Log for TrustyLogger {
enabled(&self, metadata: &Metadata) -> bool100 fn enabled(&self, metadata: &Metadata) -> bool {
101 metadata.level() <= self.config.log_level
102 }
103
log(&self, record: &Record)104 fn log(&self, record: &Record) {
105 if self.enabled(record.metadata()) {
106 let message_to_print = match &self.config.custom_format {
107 Some(log_function) => log_function(record),
108 None => default_log_function(record),
109 };
110 let _ = stderr().write(message_to_print.as_bytes());
111 }
112 }
113
flush(&self)114 fn flush(&self) {}
115 }
116
default_log_function(record: &Record) -> String117 fn default_log_function(record: &Record) -> String {
118 format!("{} - {}\n", record.level(), record.args())
119 }
120
121 static LOGGER: OnceLock<TrustyLogger> = OnceLock::new();
122
init()123 pub fn init() {
124 init_with_config(TrustyLoggerConfig::default());
125 }
126
init_with_config(config: TrustyLoggerConfig)127 pub fn init_with_config(config: TrustyLoggerConfig) {
128 let log_level_filter = config.log_level.to_level_filter();
129 let global_logger = LOGGER.get_or_init(|| TrustyLogger::new(config));
130 log::set_logger(global_logger).expect("Could not set global logger");
131 log::set_max_level(log_level_filter);
132 }
133