add logger
This commit is contained in:
parent
1628a9c690
commit
7c672c32fe
@ -8,11 +8,14 @@ edition = "2024"
|
||||
mace = { path = "/home/workspace/gits/github/mace"}
|
||||
clap = { version = "4.5.42", features = ["derive"] }
|
||||
rand = "0.9.2"
|
||||
log = "0.4.22"
|
||||
coreid = { path = "coreid"}
|
||||
logger = { path = "logger"}
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[profile.release-with-symbol]
|
||||
inherits = "release"
|
||||
|
||||
2
logger/.gitignore
vendored
Normal file
2
logger/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
12
logger/Cargo.toml
Normal file
12
logger/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "logger"
|
||||
version = "0.1.1"
|
||||
edition = "2024"
|
||||
authors = ["abbytsing@gmail.com"]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libc = "0.2.161"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
log = "0.4.22"
|
||||
298
logger/src/lib.rs
Normal file
298
logger/src/lib.rs
Normal file
@ -0,0 +1,298 @@
|
||||
use log::{LevelFilter, Metadata, Record};
|
||||
use std::cell::OnceCell;
|
||||
use std::io::Write;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use std::ptr::addr_of_mut;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
thread_local! {
|
||||
static G_TID: OnceCell<i32> = OnceCell::new();
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
static G_ID: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(1);
|
||||
|
||||
static mut G_LOGGER: Logger = Logger {
|
||||
mtx_shard: [const { Mutex::new(()) }; 8],
|
||||
sink: Vec::new(),
|
||||
abort_on_error: AtomicBool::new(false),
|
||||
};
|
||||
static G_INIIED: Mutex<bool> = Mutex::new(false);
|
||||
|
||||
const G_CONSOLE: &'static str = "console";
|
||||
const G_FILE: &'static str = "file";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn get_tid() -> i32 {
|
||||
G_TID.with(|x| *x.get_or_init(|| unsafe { libc::gettid() }))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn get_tid() -> i32 {
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
G_TID.with(|x| *x.get_or_init(|| G_ID.fetch_add(1, Relaxed)))
|
||||
}
|
||||
|
||||
/// a simple sync logger which impl log::Log
|
||||
pub struct Logger {
|
||||
mtx_shard: [Mutex<()>; 8],
|
||||
sink: Vec<SinkHandle>,
|
||||
abort_on_error: AtomicBool,
|
||||
}
|
||||
|
||||
struct SinkHandle {
|
||||
raw: *mut dyn Sink,
|
||||
}
|
||||
|
||||
unsafe impl Send for SinkHandle {}
|
||||
unsafe impl Sync for SinkHandle {}
|
||||
|
||||
impl SinkHandle {
|
||||
fn new<T>(x: T) -> Self
|
||||
where
|
||||
T: Sink + 'static,
|
||||
{
|
||||
let x = Box::new(x);
|
||||
let raw = Box::into_raw(x);
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
fn as_mut(&self) -> &mut dyn Sink {
|
||||
unsafe { &mut *self.raw }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SinkHandle {
|
||||
type Target = dyn Sink;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.raw }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SinkHandle {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Sink: Send + Sync {
|
||||
fn sink(&mut self, str: &String);
|
||||
|
||||
fn flush(&mut self);
|
||||
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl log::Log for Logger {
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
let s = format!(
|
||||
"{} {} [{}] {}:{} {}\n",
|
||||
chrono::Local::now().format("%Y-%m-%d %H:%M:%S.%6f"),
|
||||
get_tid(),
|
||||
record.level().as_str(),
|
||||
record.file().unwrap(),
|
||||
record.line().unwrap(),
|
||||
record.args()
|
||||
);
|
||||
let _lk = self.lock();
|
||||
|
||||
for p in &self.sink {
|
||||
p.as_mut().sink(&s);
|
||||
}
|
||||
|
||||
if record.level() == log::LevelFilter::Error && self.should_abort() {
|
||||
let bt = std::backtrace::Backtrace::force_capture();
|
||||
let buf = format!("{}", bt);
|
||||
for p in &self.sink {
|
||||
p.as_mut().sink(&buf);
|
||||
}
|
||||
std::process::abort();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
let _lk = self.lock();
|
||||
for p in &self.sink {
|
||||
p.as_mut().flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Console {}
|
||||
|
||||
impl Console {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: file rolling is not support at present
|
||||
struct File {
|
||||
w: std::fs::File,
|
||||
}
|
||||
|
||||
impl File {
|
||||
fn new(path: impl AsRef<Path>, trunc: bool) -> Result<Self, std::io::Error> {
|
||||
let mut ops = std::fs::File::options();
|
||||
ops.write(true).create(true);
|
||||
if trunc {
|
||||
ops.truncate(true);
|
||||
} else {
|
||||
ops.append(true);
|
||||
}
|
||||
match ops.open(path) {
|
||||
Err(e) => Err(e),
|
||||
Ok(f) => Ok(Self { w: f }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sink for Console {
|
||||
fn sink(&mut self, str: &String) {
|
||||
std::io::stdout().write(str.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
G_CONSOLE
|
||||
}
|
||||
}
|
||||
|
||||
impl Sink for File {
|
||||
fn sink(&mut self, str: &String) {
|
||||
self.w.write(str.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
self.w.flush().unwrap();
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
G_FILE
|
||||
}
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
fn is_set() -> bool {
|
||||
*G_INIIED.lock().unwrap()
|
||||
}
|
||||
pub fn init() -> &'static mut Self {
|
||||
if !Self::is_set() {
|
||||
*G_INIIED.lock().unwrap() = true;
|
||||
log::set_logger(Self::get()).unwrap();
|
||||
log::set_max_level(LevelFilter::Trace);
|
||||
}
|
||||
return Self::get();
|
||||
}
|
||||
|
||||
pub fn get() -> &'static mut Self {
|
||||
unsafe {
|
||||
let a = addr_of_mut!(G_LOGGER);
|
||||
return &mut *a;
|
||||
}
|
||||
}
|
||||
|
||||
fn exist(&self, sink: &'static str) -> Option<&mut Self> {
|
||||
let _lk = self.mtx_shard[0].lock().unwrap();
|
||||
for i in &self.sink {
|
||||
if i.name() == sink {
|
||||
return Some(Self::get());
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn should_abort(&self) -> bool {
|
||||
self.abort_on_error.load(Relaxed)
|
||||
}
|
||||
|
||||
fn lock(&self) -> MutexGuard<()> {
|
||||
self.mtx_shard[get_tid() as usize & (self.mtx_shard.len() - 1)]
|
||||
.lock()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn abort_on_error(&mut self, flag: bool) -> &mut Self {
|
||||
self.abort_on_error.store(flag, Relaxed);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_console(&mut self) -> &mut Self {
|
||||
if self.exist(G_CONSOLE).is_none() {
|
||||
self.sink.push(SinkHandle::new(Console::new()));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_file(&mut self, path: impl AsRef<Path>, trunc: bool) -> Option<&mut Self> {
|
||||
if self.exist(G_FILE).is_none() {
|
||||
match File::new(&path, trunc) {
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"can't open {}, error {}",
|
||||
path.as_ref().to_str().unwrap(),
|
||||
e.to_string()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Ok(f) => {
|
||||
self.sink.push(SinkHandle::new(f));
|
||||
return Some(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn remove_impl(&mut self, name: &'static str) {
|
||||
let _lk = self.mtx_shard[0].lock().unwrap();
|
||||
for (idx, s) in self.sink.iter().enumerate() {
|
||||
if s.name() == name {
|
||||
self.sink.remove(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_file(&mut self) {
|
||||
self.remove_impl(G_FILE);
|
||||
}
|
||||
|
||||
pub fn remove_console(&mut self) {
|
||||
self.remove_impl(G_CONSOLE);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Logger {
|
||||
fn drop(&mut self) {
|
||||
let _lk = self.lock();
|
||||
for p in &self.sink {
|
||||
p.as_mut().flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::Logger;
|
||||
|
||||
#[test]
|
||||
fn test_console() {
|
||||
let l = Logger::init();
|
||||
|
||||
let p = log::logger() as *const dyn log::Log;
|
||||
let q = &*l as *const dyn log::Log;
|
||||
assert!(std::ptr::addr_eq(p, q));
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
use clap::Parser;
|
||||
use logger::Logger;
|
||||
use mace::{Mace, Options};
|
||||
use rand::prelude::*;
|
||||
use std::path::Path;
|
||||
@ -35,6 +36,8 @@ struct Args {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Logger::init().add_file("/tmp/x.log", true);
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
let args = Args::parse();
|
||||
|
||||
let path = Path::new(&args.path);
|
||||
@ -108,6 +111,7 @@ fn main() {
|
||||
let val = value.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
coreid::bind_core(tid);
|
||||
barrier.wait();
|
||||
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user