#![deny(missing_docs)]
#![cfg_attr(feature="nightly", feature(libc))]
#[macro_use]
extern crate log;
extern crate libc;
use std::fs::{OpenOptions, File};
use std::io::{Write, self};
use std::sync::Mutex;
use std::env;
use log::{Log, Metadata, Record, Level, LevelFilter, SetLoggerError};
pub struct KernelLog {
kmsg: Mutex<File>,
maxlevel: LevelFilter
}
impl KernelLog {
pub fn new() -> io::Result<KernelLog> {
KernelLog::with_level(LevelFilter::Trace)
}
pub fn from_env() -> io::Result<KernelLog> {
match env::var("KERNLOG_LEVEL") {
Err(_) => KernelLog::new(),
Ok(s) => match s.parse() {
Ok(filter) => KernelLog::with_level(filter),
Err(_) => KernelLog::new(),
}
}
}
pub fn with_level(filter: LevelFilter) -> io::Result<KernelLog> {
Ok(KernelLog {
kmsg: Mutex::new(OpenOptions::new().write(true).open("/dev/kmsg")?),
maxlevel: filter
})
}
}
impl Log for KernelLog {
fn enabled(&self, meta: &Metadata) -> bool {
meta.level() <= self.maxlevel
}
fn log(&self, record: &Record) {
if record.level() > self.maxlevel {
return;
}
let level: u8 = match record.level() {
Level::Error => 3,
Level::Warn => 4,
Level::Info => 5,
Level::Debug => 6,
Level::Trace => 7,
};
let mut buf = Vec::new();
writeln!(buf, "<{}>{}[{}]: {}", level, record.target(),
unsafe { ::libc::getpid() },
record.args()).unwrap();
if let Ok(mut kmsg) = self.kmsg.lock() {
let _ = kmsg.write(&buf);
let _ = kmsg.flush();
}
}
fn flush(&self) {}
}
#[derive(Debug)]
pub enum KernelLogInitError {
Io(io::Error),
Log(SetLoggerError)
}
impl std::fmt::Display for KernelLogInitError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
KernelLogInitError::Io(err) => err.fmt(f),
KernelLogInitError::Log(err) => err.fmt(f),
}
}
}
impl std::error::Error for KernelLogInitError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
KernelLogInitError::Io(err) => Some(err),
KernelLogInitError::Log(err) => Some(err),
}
}
}
impl From<SetLoggerError> for KernelLogInitError {
fn from(err: SetLoggerError) -> Self {
KernelLogInitError::Log(err)
}
}
impl From<io::Error> for KernelLogInitError {
fn from(err: io::Error) -> Self {
KernelLogInitError::Io(err)
}
}
pub fn init() -> Result<(), KernelLogInitError> {
let klog = KernelLog::from_env()?;
let maxlevel = klog.maxlevel;
log::set_boxed_logger(Box::new(klog))?;
log::set_max_level(maxlevel);
Ok(())
}
#[cfg(test)]
mod tests {
use super::{KernelLog, init};
#[test]
fn log_to_kernel() {
init().unwrap();
debug!("hello, world!");
}
}