Skip to content

Commit d2c8ea2

Browse files
committed
feat(logging): add extended log filtering
Allows log filters of different levels to be applied on a per module basis. It also allows filtering by regex.
1 parent a365aba commit d2c8ea2

4 files changed

Lines changed: 64 additions & 34 deletions

File tree

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,15 @@ document-features = { version = "0.2", optional = true }
298298
embedded-io = { version = "0.7", features = ["alloc"] }
299299
endian-num = { version = "0.2", optional = true, features = ["linux-types"] }
300300
enum_dispatch = "0.3"
301+
env_filter = { version = "1.0.0", default-features = false }
301302
fdt = { version = "0.1", features = ["pretty-printing"] }
302303
free-list = "0.3"
303304
fuse-abi = { version = "0.2", optional = true, features = ["linux"] }
304305
hashbrown = { version = "0.16", default-features = false }
305306
heapless = "0.9"
306307
hermit-entry = { version = "0.10.9", features = ["kernel"] }
307308
hermit-sync = "0.1"
308-
log = { version = "0.4", default-features = false }
309+
log = { version = "0.4", default-features = false, features = ["kv"] }
309310
mem-barrier = { version = "0.1.0", optional = true, features = ["nightly"] }
310311
num_enum = { version = "0.7", default-features = false }
311312
pci-ids = { version = "0.2", optional = true }
@@ -413,6 +414,7 @@ unreadable_literal = "warn"
413414

414415
[patch.crates-io]
415416
safe-mmio = { git = "https://github.com/hermit-os/safe-mmio", branch = "be" }
417+
env_filter = { git = "https://github.com/cagatay-y/env_logger", branch = "feature/filter-no_std" }
416418

417419
[profile.profiling]
418420
inherits = "release"

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,18 @@ If you want to build the kernel for riscv64, please use `riscv64`.
3737

3838
### Control the kernel messages verbosity
3939

40-
This kernel uses the lightweight logging crate [log](https://github.com/rust-lang/log) to print kernel messages.
41-
The environment variable `HERMIT_LOG_LEVEL_FILTER` controls the verbosity.
42-
You can change it by setting it at compile time to a string matching the name of a [LevelFilter](https://docs.rs/log/0.4.8/log/enum.LevelFilter.html).
43-
If the variable is not set, or the name doesn't match, then `LevelFilter::Info` is used by default.
40+
This kernel uses the lightweight logging crate [log](https://github.com/rust-lang/log) to print kernel messages. The
41+
environment variable `HERMIT_LOG_LEVEL_FILTER` controls the verbosity and follows [the env_logger
42+
format](https://docs.rs/env_logger/latest/env_logger/) but without the regex support. You can change it per module by
43+
setting it at compile time to a string in the format `[target][=level][,...]`, where the level is a string matching the
44+
name of a [LevelFilter](https://docs.rs/log/0.4.8/log/enum.LevelFilter.html). If the target is omitted, the level is set
45+
as the global level. If the level is omitted, logs of all levels are printed for the target.
46+
47+
If the variable is not set, or it does not provide a global level, then `LevelFilter::Info` is used as the global level
48+
by default.
4449

4550
```sh
46-
$ HERMIT_LOG_LEVEL_FILTER=Debug cargo xtask build --arch x86_64
51+
HERMIT_LOG_LEVEL_FILTER='smoltcp,virtio=info' cargo xtask build --arch x86_64
4752
```
4853

4954
## Credits

src/logging.rs

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,27 @@ use core::sync::atomic::{AtomicBool, Ordering};
33
use core::time::Duration;
44

55
use anstyle::AnsiColor;
6+
use hermit_sync::OnceCell;
67
use log::{Level, LevelFilter, Metadata, Record};
78

89
pub static KERNEL_LOGGER: KernelLogger = KernelLogger::new();
10+
const ARENA_SIZE: usize = 4096;
11+
static mut ARENA: [u8; ARENA_SIZE] = [0; _];
912

1013
const TIME_SEC_WIDTH: usize = 5;
1114
const TIME_SUBSEC_WIDTH: usize = 6;
1215

1316
/// Data structure to filter kernel messages
1417
pub struct KernelLogger {
1518
time: AtomicBool,
19+
filter: OnceCell<env_filter::Filter>,
1620
}
1721

1822
impl KernelLogger {
1923
pub const fn new() -> Self {
2024
Self {
2125
time: AtomicBool::new(false),
26+
filter: OnceCell::new(),
2227
}
2328
}
2429

@@ -45,6 +50,23 @@ impl log::Log for KernelLogger {
4550
return;
4651
}
4752

53+
let target = record.target();
54+
let (crate_, modules) = target.split_once("::").unwrap_or((target, ""));
55+
let (_modules, module) = modules.rsplit_once("::").unwrap_or(("", modules));
56+
let target = if !module.is_empty() && crate_ == "hermit" {
57+
module
58+
} else {
59+
crate_
60+
};
61+
62+
if self.filter.get().is_some_and(|filter| {
63+
// We want the target that we match against to match the target we display.
64+
let record = record.to_builder().target(target).build();
65+
!filter.matches(&record)
66+
}) {
67+
return;
68+
}
69+
4870
let format_time = if self.time() {
4971
let time = Duration::from_micros(crate::processor::get_timer_ticks());
5072
format_args!(
@@ -58,14 +80,6 @@ impl log::Log for KernelLogger {
5880
let core_id = crate::arch::core_local::core_id();
5981
let level = ColorLevel(record.level());
6082

61-
let target = record.target();
62-
let (crate_, modules) = target.split_once("::").unwrap_or((target, ""));
63-
let (_modules, module) = modules.rsplit_once("::").unwrap_or(("", modules));
64-
let target = if !module.is_empty() && crate_ == "hermit" {
65-
module
66-
} else {
67-
crate_
68-
};
6983
let format_target = format_args!(" {target:<10}");
7084

7185
let args = record.args();
@@ -101,29 +115,29 @@ fn no_color() -> bool {
101115
}
102116

103117
pub unsafe fn init() {
118+
#[cfg(target_os = "none")]
119+
unsafe {
120+
crate::mm::ALLOCATOR
121+
.lock()
122+
.claim((&raw mut ARENA).cast(), ARENA_SIZE)
123+
.unwrap()
124+
};
125+
104126
log::set_logger(&KERNEL_LOGGER).expect("Can't initialize logger");
105127
// Determines LevelFilter at compile time
106128
let log_level: Option<&'static str> = option_env!("HERMIT_LOG_LEVEL_FILTER");
107-
let mut max_level = LevelFilter::Info;
108-
109-
if let Some(log_level) = log_level {
110-
max_level = if log_level.eq_ignore_ascii_case("off") {
111-
LevelFilter::Off
112-
} else if log_level.eq_ignore_ascii_case("error") {
113-
LevelFilter::Error
114-
} else if log_level.eq_ignore_ascii_case("warn") {
115-
LevelFilter::Warn
116-
} else if log_level.eq_ignore_ascii_case("info") {
117-
LevelFilter::Info
118-
} else if log_level.eq_ignore_ascii_case("debug") {
119-
LevelFilter::Debug
120-
} else if log_level.eq_ignore_ascii_case("trace") {
121-
LevelFilter::Trace
122-
} else {
123-
error!("Could not parse HERMIT_LOG_LEVEL_FILTER, falling back to `info`.");
124-
LevelFilter::Info
125-
};
126-
}
129+
let max_level = if let Some(log_level) = log_level {
130+
let filter = env_filter::Builder::new()
131+
// The default. It may get overwritten by the parsed filter if it has a global level.
132+
.filter_level(LevelFilter::Info)
133+
.parse(log_level)
134+
.build();
135+
let max_level = filter.filter();
136+
KERNEL_LOGGER.filter.set(filter).unwrap();
137+
max_level
138+
} else {
139+
LevelFilter::Info
140+
};
127141

128142
log::set_max_level(max_level);
129143
}

0 commit comments

Comments
 (0)