The main limitation of this simple implementation is that all events/spans are deliberately sinkholed by the MockSubscriber
, as a most off-the-shelf subscribers depend on the standard library. In the future, it would be nice to get those to work properly, but this simple approach should work fine for now.
S2RR5DIKAB5T5J5LL6NJKNPOF3J7HJBPIS5KODP4RFQKRUB2MPDAC
#![no_std]
extern crate alloc;
use alloc::string::{String, ToString};
use core::fmt::Write;
use lazy_static::lazy_static;
use tracing_core::Subscriber;
use tracing_subscriber::Layer;
use uart_16550::SerialPort;
use x86_64::instructions::interrupts;
lazy_static! {
pub static ref SERIAL1: spin::Mutex<SerialPort> = {
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
serial_port.init();
spin::Mutex::new(serial_port)
};
}
/// Tracing subscriber that does nothing
// TODO: figure out how to use standard library so can replace with actual subscribers
pub struct MockSubscriber;
/// A layer that prints all events to the serial console
pub struct SerialLayer;
/// Simple visitor that formats any fields attached to spans
struct PrintVisitor(String);
impl<S> Layer<S> for SerialLayer
where
S: tracing_core::Subscriber,
{
fn on_event(
&self,
event: &tracing_core::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let metadata = event.metadata();
let mut msg = metadata.level().to_string();
// Attach any location context
if let Some(location) = metadata.file().or(metadata.module_path()) {
msg.push(' ');
msg.push_str(location);
if let Some(line) = metadata.line() {
msg.push(':');
msg.push_str(&line.to_string());
}
}
let mut visitor = PrintVisitor(String::new());
event.record(&mut visitor);
msg.push_str(&visitor.0);
msg.push('\n');
interrupts::without_interrupts(|| {
SERIAL1
.lock()
.write_str(&msg)
.expect("Printing to serial failed");
});
}
}
impl tracing::field::Visit for PrintVisitor {
fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn core::fmt::Debug) {
self.0.push(' ');
if field.name() != "message" {
self.0.push_str(field.name());
self.0.push('=');
}
write!(self.0, "{:#?}", value).unwrap();
}
}
impl Subscriber for MockSubscriber {
fn enabled(&self, _metadata: &tracing_core::Metadata<'_>) -> bool {
true
}
fn new_span(&self, _span: &tracing_core::span::Attributes<'_>) -> tracing_core::span::Id {
tracing_core::span::Id::from_u64(0)
}
fn record(&self, _span: &tracing_core::span::Id, _values: &tracing_core::span::Record<'_>) {
()
}
fn record_follows_from(
&self,
_span: &tracing_core::span::Id,
_follows: &tracing_core::span::Id,
) {
()
}
fn event(&self, _event: &tracing_core::Event<'_>) {
()
}
fn enter(&self, _span: &tracing_core::span::Id) {
()
}
fn exit(&self, _span: &tracing_core::span::Id) {
()
}
}
[package]
name = "tracing_serial"
version = "0.1.0"
edition = "2021"
[dependencies]
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
spin = "0.9.8"
tracing = { version = "0.1.40", default-features = false }
tracing-core = { version = "0.1.32", default-features = false }
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
"alloc",
] }
uart_16550 = "0.3.0"
x86_64 = "0.14.11"
tracing_serial = { path = "../tracing_serial" }
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"tracing-core",
]
[[package]]
name = "tracing_serial"
version = "0.1.0"
dependencies = [
"lazy_static",
"spin 0.9.8",
"tracing",
"tracing-core",
"tracing-subscriber",
"uart_16550",
"x86_64",
]
[[package]]
name = "uart_16550"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dc00444796f6c71f47c85397a35e9c4dbf9901902ac02386940d178e2b78687"
dependencies = [
"bitflags 1.3.2",
"rustversion",
"x86",
]
[[package]]