use crate::Level;
use crate::Record;
use crate::handler::Handler;
use crate::processor::Processor;
use chrono::{DateTime, Utc};
use chrono_tz::Tz;
use serde_json::Value;
pub struct Logger {
pub name: String,
pub timezone: String,
pub depth: usize,
pub fiber_depth: usize,
pub cycles: bool,
handlers: Vec<Box<dyn Handler>>,
processors: Vec<Box<dyn Processor>>,
}
pub enum LoggerComponent {
Handler(Box<dyn Handler>),
Processor(Box<dyn Processor>),
}
impl Logger {
pub fn new(name: impl Into<String>, timezone: impl Into<String>) -> Self {
Self {
name: name.into(),
timezone: timezone.into(),
depth: 0,
fiber_depth: 0,
cycles: false,
handlers: vec![],
processors: vec![],
}
}
pub fn emergency(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Emergency, message, context);
}
pub fn alert(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Alert, message, context);
}
pub fn critical(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Critical, message, context);
}
pub fn error(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Error, message, context);
}
pub fn warning(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Warning, message, context);
}
pub fn notice(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Notice, message, context);
}
pub fn info(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Info, message, context);
}
pub fn debug(&mut self, message: &str, context: Option<Value>) {
self.write(Level::Debug, message, context);
}
pub fn log(&mut self, level: Level, message: &str, context: Option<Value>) -> bool {
self.write(level, message, context)
}
pub fn write(&mut self, level: Level, message: &str, context: Option<Value>) -> bool {
let log_depth = if self.cycles {
self.fiber_depth += 1;
self.fiber_depth
} else {
self.depth += 1;
self.depth
};
if log_depth == 3 {
self.warning(
"A possible infinite logging loop was detected and aborted. \
It appears some of your handler code is triggering logging.",
None,
);
return false;
} else if log_depth >= 5 {
return false;
}
let tz: Tz = self.timezone.parse().unwrap_or(chrono_tz::UTC);
let timestamp: DateTime<Tz> = Utc::now().with_timezone(&tz);
let mut record = Record::new(level, message, &self.name, context, timestamp);
let mut handled = false;
for processor in &self.processors {
processor.process(&mut record);
}
for handler in self.handlers.iter_mut() {
if handler.log(&record) {
handled = true;
break;
}
}
if self.cycles {
if self.fiber_depth > 0 {
self.fiber_depth -= 1;
}
} else {
if self.depth > 0 {
self.depth -= 1;
}
}
handled
}
pub fn push(&mut self, component: LoggerComponent) {
match component {
LoggerComponent::Handler(handler) => {
self.handlers.push(handler);
self.handlers
.sort_by(|b, a| b.severity().cmp(&a.severity()));
}
LoggerComponent::Processor(processor) => {
self.processors.push(processor);
self.processors
.sort_by(|b, a| b.severity().cmp(&a.severity()));
}
}
}
pub fn pop(&mut self, is_handler: bool) -> Option<LoggerComponent> {
if is_handler {
self.handlers.pop().map(LoggerComponent::Handler)
} else {
self.processors.pop().map(LoggerComponent::Processor)
}
}
pub fn flush(&mut self) {
self.handlers.clear();
self.processors.clear();
self.depth = 0;
self.fiber_depth = 0;
self.cycles = false;
}
pub fn flush_handlers(&mut self) {
self.handlers.clear();
}
pub fn flush_processors(&mut self) {
self.processors.clear();
}
pub fn flush_depth(&mut self) {
self.depth = 0;
self.fiber_depth = 0;
self.cycles = false;
}
}