Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use log::Record;
9use spin::LazyLock;
10use tracing::field;
11use tracing_core::{Callsite, Collect, Event, Kind, Level, Metadata, dispatch, identify_callsite};
12
13impl log::Log for super::Subscriber {
14 fn enabled(&self, metadata: &log::Metadata) -> bool {
15 if let Some(level_filter) = self.max_level_hint() {
16 loglevel_to_level(metadata.level()) <= level_filter
17 } else {
18 false
19 }
20 }
21
22 fn log(&self, record: &Record) {
23 dispatch::get_default(|dispatch| {
24 let (cs, keys, meta) = loglevel_to_callsite(record.level());
25 let cs_id = identify_callsite!(cs);
26
27 let filter_meta = Metadata::new(
28 "log record",
29 record.target(),
30 loglevel_to_level(record.level()),
31 None,
32 None,
33 None,
34 field::FieldSet::new(FIELD_NAMES, cs_id),
35 Kind::EVENT,
36 );
37
38 if !dispatch.enabled(&filter_meta) {
39 return;
40 }
41
42 let log_module = record.module_path();
43 let log_file = record.file();
44 let log_line = record.line();
45
46 let module = log_module.as_ref().map(|s| s as &dyn field::Value);
47 let file = log_file.as_ref().map(|s| s as &dyn field::Value);
48 let line = log_line.as_ref().map(|s| s as &dyn field::Value);
49
50 dispatch.event(&Event::new(
51 meta,
52 &meta.fields().value_set(&[
53 (&keys.message, Some(record.args() as &dyn field::Value)),
54 (&keys.target, Some(&record.target())),
55 (&keys.module, module),
56 (&keys.file, file),
57 (&keys.line, line),
58 ]),
59 ));
60 });
61 }
62
63 fn flush(&self) {}
64}
65
66struct Fields {
67 message: field::Field,
68 target: field::Field,
69 module: field::Field,
70 file: field::Field,
71 line: field::Field,
72}
73
74static FIELD_NAMES: &[&str] = &[
75 "message",
76 "log.target",
77 "log.module_path",
78 "log.file",
79 "log.line",
80];
81
82impl Fields {
83 fn new(cs: &'static dyn Callsite) -> Self {
84 let fieldset = cs.metadata().fields();
85 let message = fieldset.field("message").unwrap();
86 let target = fieldset.field("log.target").unwrap();
87 let module = fieldset.field("log.module_path").unwrap();
88 let file = fieldset.field("log.file").unwrap();
89 let line = fieldset.field("log.line").unwrap();
90 Fields {
91 message,
92 target,
93 module,
94 file,
95 line,
96 }
97 }
98}
99
100fn loglevel_to_level(level: log::Level) -> Level {
101 match level {
102 log::Level::Error => Level::ERROR,
103 log::Level::Warn => Level::WARN,
104 log::Level::Info => Level::INFO,
105 log::Level::Debug => Level::DEBUG,
106 log::Level::Trace => Level::TRACE,
107 }
108}
109
110macro_rules! log_cs {
111 ($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
112 struct $ty;
113 static $cs: $ty = $ty;
114 static $meta: Metadata<'static> = Metadata::new(
115 "log event",
116 "log",
117 $level,
118 ::core::option::Option::None,
119 ::core::option::Option::None,
120 ::core::option::Option::None,
121 ::tracing_core::field::FieldSet::new(
122 FIELD_NAMES,
123 ::tracing_core::identify_callsite!(&$cs),
124 ),
125 ::tracing_core::metadata::Kind::EVENT,
126 );
127
128 impl tracing_core::callsite::Callsite for $ty {
129 fn set_interest(&self, _: ::tracing_core::Interest) {}
130 fn metadata(&self) -> &'static Metadata<'static> {
131 &$meta
132 }
133 }
134 };
135}
136
137log_cs!(Level::TRACE, TRACE_CS, TRACE_META, TraceCallsite);
138log_cs!(Level::DEBUG, DEBUG_CS, DEBUG_META, DebugCallsite);
139log_cs!(Level::INFO, INFO_CS, INFO_META, InfoCallsite);
140log_cs!(Level::WARN, WARN_CS, WARN_META, WarnCallsite);
141log_cs!(Level::ERROR, ERROR_CS, ERROR_META, ErrorCallsite);
142
143static TRACE_FIELDS: LazyLock<Fields> = LazyLock::new(|| Fields::new(&TRACE_CS));
144static DEBUG_FIELDS: LazyLock<Fields> = LazyLock::new(|| Fields::new(&DEBUG_CS));
145static INFO_FIELDS: LazyLock<Fields> = LazyLock::new(|| Fields::new(&INFO_CS));
146static WARN_FIELDS: LazyLock<Fields> = LazyLock::new(|| Fields::new(&WARN_CS));
147static ERROR_FIELDS: LazyLock<Fields> = LazyLock::new(|| Fields::new(&ERROR_CS));
148
149fn loglevel_to_callsite(
150 level: log::Level,
151) -> (
152 &'static dyn Callsite,
153 &'static Fields,
154 &'static Metadata<'static>,
155) {
156 match level {
157 log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
158 log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
159 log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
160 log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
161 log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
162 }
163}