// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use std::io::prelude::*; use std::sync::Mutex; use crate::unix::gettid; use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; fn format_now() -> String { let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0, }; let mut ti: libc::tm = unsafe { std::mem::zeroed() }; unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts); libc::localtime_r(&ts.tv_sec, &mut ti); } format!( "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}", ti.tm_year + 1900, ti.tm_mon + 1, ti.tm_mday, ti.tm_hour, ti.tm_min, ti.tm_sec, ts.tv_nsec ) } /// Format like "%year-%mon-%dayT%hour:%min:%sec.%nsec struct VmLogger { handler: Option<Mutex<Box<dyn Write + Send>>>, level: Level, } impl Log for VmLogger { fn enabled(&self, metadata: &Metadata) -> bool { self.handler.is_some() && metadata.level() <= self.level } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { let pid = unsafe { libc::getpid() }; let tid = gettid(); self.handler.as_ref().map(|writer| match record.level() { Level::Error => writer.lock().unwrap().write_fmt(format_args!( "{:<5}: [{}][{}][{}: {}]:{}: {}\n", format_now(), pid, tid, record.file().unwrap_or(""), record.line().unwrap_or(0), record.level(), record.args() )), _ => writer.lock().unwrap().write_fmt(format_args!( "{:<5}: [{}][{}]:{}: {}\n", format_now(), pid, tid, record.level(), record.args() )), }); } } fn flush(&self) {} } pub fn init_vm_logger( level: Option<Level>, logfile: Option<Box<dyn Write + Send>>, ) -> Result<(), log::SetLoggerError> { let buffer = logfile.map(Mutex::new); let logger = VmLogger { level: level.unwrap_or(Level::Info), handler: buffer, }; log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(LevelFilter::Trace)) } pub fn init_logger_with_env(logfile: Option<Box<dyn Write + Send>>) -> Result<(), SetLoggerError> { let level = match std::env::var("STRATOVIRT_LOG_LEVEL") { Ok(l) => match l.to_lowercase().as_str() { "trace" => Level::Trace, "debug" => Level::Debug, "info" => Level::Info, "warn" => Level::Warn, _ => Level::Error, }, _ => Level::Error, }; init_vm_logger(Some(level), logfile)?; Ok(()) }