sel4_logging/
lib.rs

1//
2// Copyright 2023, Colias Group, LLC
3//
4// SPDX-License-Identifier: BSD-2-Clause
5//
6
7#![no_std]
8
9use core::fmt::{self, Write};
10
11use log::{Log, Metadata, Record, SetLoggerError};
12
13pub use log::{self, LevelFilter};
14
15mod synchronized;
16
17pub use synchronized::SynchronizedLogger;
18
19pub struct Logger {
20    pub level_filter: LevelFilter,
21    pub filter: fn(&Metadata) -> bool,
22    pub fmt: FmtRecordFn,
23    pub write: fn(&str),
24    pub flush: fn(),
25}
26
27pub type FmtRecordFn = fn(&Record, &mut fmt::Formatter) -> fmt::Result;
28
29pub const FMT_RECORD_DEFAULT: FmtRecordFn = fmt_with_module;
30
31impl Logger {
32    pub const fn const_default() -> Self {
33        Self {
34            level_filter: LevelFilter::Warn,
35            filter: |_| true,
36            fmt: FMT_RECORD_DEFAULT,
37            write: |_| (),
38            flush: || (),
39        }
40    }
41
42    pub fn level_filter(&self) -> LevelFilter {
43        self.level_filter
44    }
45
46    pub fn set_max_level(&self) {
47        log::set_max_level(self.level_filter());
48    }
49
50    pub fn set(&'static self) -> Result<(), SetLoggerError> {
51        self.set_max_level();
52        log::set_logger(self)?;
53        Ok(())
54    }
55}
56
57impl Log for Logger {
58    fn enabled(&self, metadata: &Metadata) -> bool {
59        metadata.level() <= self.level_filter && (self.filter)(metadata)
60    }
61
62    fn log(&self, record: &Record) {
63        if self.enabled(record.metadata()) {
64            let mut writer = WriteWrapper(self.write);
65            let wrapped = DisplayWrapper {
66                fmt: self.fmt,
67                record,
68            };
69            writeln!(writer, "{wrapped}").unwrap()
70        }
71    }
72
73    fn flush(&self) {
74        (self.flush)()
75    }
76}
77
78//
79
80struct WriteWrapper(fn(&str));
81
82impl fmt::Write for WriteWrapper {
83    fn write_str(&mut self, s: &str) -> fmt::Result {
84        (self.0)(s);
85        Ok(())
86    }
87}
88
89struct DisplayWrapper<'a> {
90    fmt: FmtRecordFn,
91    record: &'a Record<'a>,
92}
93
94impl fmt::Display for DisplayWrapper<'_> {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        (self.fmt)(self.record, f)
97    }
98}
99
100//
101
102pub struct LoggerBuilder(Logger);
103
104impl LoggerBuilder {
105    pub const fn const_default() -> Self {
106        Self(Logger::const_default())
107    }
108
109    pub const fn build(self) -> Logger {
110        self.0
111    }
112
113    pub const fn level_filter(mut self, level_filter: LevelFilter) -> Self {
114        self.0.level_filter = level_filter;
115        self
116    }
117
118    pub const fn filter(mut self, filter: fn(&Metadata) -> bool) -> Self {
119        self.0.filter = filter;
120        self
121    }
122
123    pub const fn fmt(mut self, fmt: FmtRecordFn) -> Self {
124        self.0.fmt = fmt;
125        self
126    }
127
128    pub const fn write(mut self, write: fn(&str)) -> Self {
129        self.0.write = write;
130        self
131    }
132
133    pub const fn flush(mut self, flush: fn()) -> Self {
134        self.0.flush = flush;
135        self
136    }
137}
138
139//
140
141pub fn fmt_with_module(record: &Record, f: &mut fmt::Formatter) -> fmt::Result {
142    let target = if !record.target().is_empty() {
143        record.target()
144    } else {
145        record.module_path().unwrap_or_default()
146    };
147    write!(f, "{:<5} [{}] {}", record.level(), target, record.args())
148}
149
150pub fn fmt_with_line(record: &Record, f: &mut fmt::Formatter) -> fmt::Result {
151    write!(f, "{:<5} [", record.level())?;
152    if let Some(file) = record.file() {
153        write!(f, "{file}")?;
154    } else if let Some(file) = record.file_static() {
155        write!(f, "{file}")?;
156    } else {
157        write!(f, "(?)")?;
158    }
159    write!(f, ":")?;
160    if let Some(line) = record.line() {
161        write!(f, "{line}")?;
162    } else {
163        write!(f, "(?)")?;
164    }
165    write!(f, "] {}", record.args())
166}