blob: dc270ced577d8df0cf7cbd24d19f688f75e6e843 [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
use std::sync::{Arc, Mutex};
use log::{Level, LevelFilter, Log};
const FLEXOR_TAG: &str = "flexor";
const DEFAULT_DEVICE: &str = "/dev/kmsg";
const MAX_LEVEL: LevelFilter = LevelFilter::Info;
const MAX_LOG_RECORD_SIZE: usize = 1024;
// A logger that logs to the kernel message buffer.
struct KernelLogger {
file: Arc<Mutex<File>>,
}
impl KernelLogger {
fn new() -> io::Result<Self> {
Ok(KernelLogger {
file: Arc::new(Mutex::new(
OpenOptions::new().write(true).open(DEFAULT_DEVICE)?,
)),
})
}
}
impl Log for KernelLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= MAX_LEVEL
}
fn log(&self, record: &log::Record) {
if record.level() > MAX_LEVEL {
return;
}
let level: u8 = match record.level() {
Level::Error => 3,
Level::Warn => 4,
Level::Info => 5,
Level::Debug => 6,
Level::Trace => 7,
};
let message = record.args().to_string();
let prefix = format!("<{level}>{FLEXOR_TAG}[{}]: ", unsafe {
nix::libc::getpid()
});
// The kernel log buffer can only append messages with a max size of
// 1024. For that reason we might log the message in chunks.
let msg_chunks: Vec<&str> = if message.len() < MAX_LOG_RECORD_SIZE - prefix.len() {
vec![&message]
} else {
message.lines().collect()
};
if let Ok(mut kmsg) = self.file.lock() {
for msg in msg_chunks {
_ = kmsg.write(format!("{prefix}{msg}").as_bytes());
_ = kmsg.flush();
}
}
}
fn flush(&self) {}
}
pub fn init() -> anyhow::Result<()> {
let klog = KernelLogger::new()?;
log::set_boxed_logger(Box::new(klog))?;
log::set_max_level(MAX_LEVEL);
Ok(())
}