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