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::Once;
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 mut LOGGER: Option<TrustyLogger> = None;
122 static LOGGER_INIT: Once = Once::new();
123
init()124 pub fn init() {
125 init_with_config(TrustyLoggerConfig::default());
126 }
127
init_with_config(config: TrustyLoggerConfig)128 pub fn init_with_config(config: TrustyLoggerConfig) {
129 let log_level_filter = config.log_level.to_level_filter();
130 // SAFETY: We are using Once, so the mut global will only be written once even with multiple
131 // calls
132 let global_logger = unsafe {
133 LOGGER_INIT.call_once(|| {
134 LOGGER = Some(TrustyLogger::new(config));
135 });
136 // Logger is always Some(_) at this point, so we just unwrap it
137 LOGGER.as_ref().unwrap()
138 };
139 log::set_logger(global_logger).expect("Could not set global logger");
140 log::set_max_level(log_level_filter);
141 }
142