use std::io::Write;
use std::panic;
use crate::Append;
use crate::Diagnostic;
use crate::Error;
use crate::Filter;
use crate::filter::FilterResult;
use crate::record::FilterCriteria;
use crate::record::Record;
#[derive(Debug)]
pub struct Logger {
dispatches: Vec<Dispatch>,
}
impl Logger {
pub(super) fn new(dispatches: Vec<Dispatch>) -> Self {
Self { dispatches }
}
}
impl Logger {
pub fn enabled(&self, criteria: &FilterCriteria) -> bool {
self.dispatches
.iter()
.any(|dispatch| dispatch.enabled(criteria))
}
pub fn log(&self, record: &Record) {
for dispatch in &self.dispatches {
for err in dispatch.log(record) {
handle_log_error(record, &err);
}
}
}
pub fn flush(&self) {
for dispatch in &self.dispatches {
for err in dispatch.flush() {
handle_flush_error(&err);
}
}
}
}
#[derive(Debug)]
pub(super) struct Dispatch {
filters: Vec<Box<dyn Filter>>,
diagnostics: Vec<Box<dyn Diagnostic>>,
appends: Vec<Box<dyn Append>>,
}
impl Dispatch {
pub(super) fn new(
filters: Vec<Box<dyn Filter>>,
diagnostics: Vec<Box<dyn Diagnostic>>,
appends: Vec<Box<dyn Append>>,
) -> Self {
debug_assert!(
!appends.is_empty(),
"A Dispatch must have at least one filter"
);
Self {
filters,
diagnostics,
appends,
}
}
fn enabled(&self, criteria: &FilterCriteria) -> bool {
let diagnostics = &self.diagnostics;
for filter in &self.filters {
match filter.enabled(criteria, diagnostics) {
FilterResult::Reject => return false,
FilterResult::Accept => return true,
FilterResult::Neutral => {}
}
}
true
}
fn log(&self, record: &Record) -> Vec<Error> {
let diagnostics = &self.diagnostics;
for filter in &self.filters {
match filter.matches(record, diagnostics) {
FilterResult::Reject => return vec![],
FilterResult::Accept => break,
FilterResult::Neutral => {}
}
}
let mut errors = vec![];
for append in &self.appends {
if let Err(err) = append.append(record, diagnostics) {
errors.push(err);
}
}
errors
}
fn flush(&self) -> Vec<Error> {
let mut errors = vec![];
for append in &self.appends {
if let Err(err) = append.flush() {
errors.push(err);
}
}
errors
}
}
fn handle_log_error(record: &Record, error: &Error) {
let Err(fallback_error) = write!(
std::io::stderr(),
r###"
Error perform logging.
Attempted to log: {args}
Record: {record:?}
Error: {error:?}
"###,
args = record.payload(),
record = record,
error = error,
) else {
return;
};
panic!(
r###"
Error performing stderr logging after error occurred during regular logging.
Attempted to log: {args}
Record: {record:?}
Error: {error:?}
Fallback error: {fallback_error}
"###,
args = record.payload(),
record = record,
error = error,
fallback_error = fallback_error,
);
}
fn handle_flush_error(error: &Error) {
let Err(fallback_error) = write!(
std::io::stderr(),
r###"
Error perform flush.
Error: {error:?}
"###,
) else {
return;
};
panic!(
r###"
Error performing stderr logging after error occurred during regular flush.
Error: {error:?}
Fallback error: {fallback_error}
"###,
);
}