Files
initrd/src/dklog.rs

133 lines
3.3 KiB
Rust
Raw Normal View History

2024-04-29 12:54:25 +02:00
use std::io::Write;
use std::sync::{LazyLock, Mutex};
use std::time::SystemTime;
use tokio::sync::watch;
use tokio::task::JoinSet;
pub static LOG: LazyLock<Log> = LazyLock::new(Log::new);
pub fn init() {
log::set_logger(&*LOG).expect("set_logger should not fail");
log::set_max_level(log::LevelFilter::Info);
}
pub struct Log {
start: SystemTime,
log: Mutex<Vec<u8>>,
tx: Mutex<Option<watch::Sender<usize>>>,
rx: watch::Receiver<usize>,
tasks: Mutex<Option<JoinSet<()>>>,
}
impl Log {
pub fn new() -> Self {
let (tx, rx) = watch::channel(0);
Self {
start: SystemTime::now(),
log: Mutex::new(Vec::new()),
tx: Mutex::new(Some(tx)),
rx,
tasks: Mutex::new(Some(JoinSet::new())),
}
}
pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
if let Some(tasks) = self.tasks.lock().unwrap().as_mut() {
tasks.spawn(task);
}
}
pub fn subscribe(&self) -> LogWatch {
LogWatch {
log: self,
pos: 0,
rx: self.rx.clone(),
}
}
pub async fn copy_to<W: tokio::io::AsyncWrite + Unpin>(&self, mut out: W) {
let mut log = self.subscribe();
use tokio::io::AsyncWriteExt;
while let Some(chunk) = log.next().await {
let _ = out.write_all(&chunk).await;
let _ = out.flush().await;
}
}
pub async fn close(&self) {
self.tx.lock().unwrap().take();
if let Some(tasks) = self.tasks.lock().unwrap().take() {
tasks.join_all().await;
}
}
}
impl log::Log for Log {
fn enabled(&self, metadata: &log::Metadata) -> bool {
log::max_level().to_level().unwrap_or(log::Level::Info) >= metadata.level()
}
fn log(&self, record: &log::Record) {
if !self.enabled(record.metadata()) {
return;
}
let ts = self.start.elapsed().unwrap_or_default();
use log::Level::*;
let level = match record.level() {
Trace => "\x1b[2mTRC\x1b[0m",
Debug => "\x1b[34mDBG\x1b[0m",
Info => "\x1b[32mINF\x1b[0m",
Warn => "\x1b[93mWRN\x1b[0m",
Error => "\x1b[91mERR\x1b[0m",
};
let Ok(mut log) = self.log.lock() else {
return;
};
writeln!(
log,
"{:3}.{:03} {level} {}",
ts.as_secs(),
ts.subsec_millis(),
record.args()
)
.expect("write to buf should not fail");
if let Some(tx) = self.tx.lock().unwrap().as_mut() {
tx.send_replace(log.len());
}
}
fn flush(&self) {
// always flushed
}
}
pub struct LogWatch<'t> {
log: &'t Log,
pos: usize,
rx: watch::Receiver<usize>,
}
impl<'t> LogWatch<'t> {
pub async fn next(&mut self) -> Option<Vec<u8>> {
loop {
let new_pos = self.rx.borrow_and_update().clone();
if new_pos <= self.pos {
if self.rx.changed().await.is_err() {
return None; // finished
}
continue;
}
let mut chunk = Vec::new();
chunk.extend(&self.log.log.lock().unwrap()[self.pos..new_pos]);
self.pos = new_pos;
return Some(chunk);
}
}
}