#![doc = include_str!("README.md")]
use crate::affinity;
use crate::channel::{Sender, channel};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex}; use std::thread;
#[derive(Clone)]
pub struct Logger {
sender: Arc<Mutex<Sender<String>>>,
dropped_count: Arc<AtomicU64>,
}
impl Logger {
pub fn new(capacity: usize) -> Self {
let (tx, rx) = channel(capacity);
let dropped = Arc::new(AtomicU64::new(0));
thread::spawn(move || {
if let Some(last_core) = affinity::get_core_ids().last() {
let _ = affinity::pin_to_core(*last_core);
}
while let Some(msg) = rx.recv() {
println!("[LOG] {}", msg);
}
});
Self {
sender: Arc::new(Mutex::new(tx)),
dropped_count: dropped,
}
}
pub fn log(&self, msg: impl Into<String>) {
if let Ok(guard) = self.sender.lock() {
if guard.try_send(msg.into()).is_err() {
self.dropped_count.fetch_add(1, Ordering::Relaxed);
}
}
}
pub fn get_dropped_count(&self) -> u64 {
self.dropped_count.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_logger_basic() {
let logger = Logger::new(16);
logger.log("Hello World");
logger.log("This is a test");
thread::sleep(Duration::from_millis(50));
assert_eq!(logger.get_dropped_count(), 0);
}
#[test]
fn test_dropped_logs_under_load() {
let logger = Logger::new(2);
for i in 0..20 {
logger.log(format!("Flood {}", i));
}
let dropped = logger.get_dropped_count();
println!("Dropped {} messages (Expected > 0)", dropped);
assert!(dropped > 0);
}
}