add logger

This commit is contained in:
abbycin 2025-08-17 08:03:54 +08:00
parent 1628a9c690
commit 7c672c32fe
Signed by: abby
GPG Key ID: B636E0F0307EF8EB
5 changed files with 319 additions and 0 deletions

View File

@ -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
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

12
logger/Cargo.toml Normal file
View 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
View 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));
}
}

View File

@ -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();
{