use crate::primary_writer::PrimaryWriter;
use crate::util::{eprint_err, ERRCODE};
use crate::writers::{FileLogWriterBuilder, LogWriter};
use crate::{FlexiLoggerError, LogSpecification};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
#[derive(Clone)]
pub struct LoggerHandle {
spec: Arc<RwLock<LogSpecification>>,
spec_stack: Vec<LogSpecification>,
primary_writer: Arc<PrimaryWriter>,
other_writers: Arc<HashMap<String, Box<dyn LogWriter>>>,
}
impl LoggerHandle {
pub(crate) fn new(
spec: Arc<RwLock<LogSpecification>>,
primary_writer: Arc<PrimaryWriter>,
other_writers: Arc<HashMap<String, Box<dyn LogWriter>>>,
) -> Self {
Self {
spec,
spec_stack: Vec::default(),
primary_writer,
other_writers,
}
}
pub(crate) fn reconfigure(&self, mut max_level: log::LevelFilter) {
for w in self.other_writers.as_ref().values() {
max_level = std::cmp::max(max_level, w.max_log_level());
}
log::set_max_level(max_level);
}
#[allow(clippy::missing_panics_doc)]
pub fn set_new_spec(&mut self, new_spec: LogSpecification) {
let max_level = new_spec.max_level();
self.spec
.write()
.map_err(|e| eprint_err(ERRCODE::Poison, "rwlock on log spec is poisoned", &e))
.unwrap()
.update_from(new_spec);
self.reconfigure(max_level);
}
pub fn parse_new_spec(&mut self, spec: &str) -> Result<(), FlexiLoggerError> {
self.set_new_spec(LogSpecification::parse(spec)?);
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn push_temp_spec(&mut self, new_spec: LogSpecification) {
self.spec_stack
.push(self.spec.read().unwrap().clone());
self.set_new_spec(new_spec);
}
pub fn parse_and_push_temp_spec<S: AsRef<str>>(
&mut self,
new_spec: S,
) -> Result<(), FlexiLoggerError> {
self.spec_stack.push(
self.spec
.read()
.map_err(|_| FlexiLoggerError::Poison)?
.clone(),
);
self.set_new_spec(LogSpecification::parse(new_spec)?);
Ok(())
}
pub fn pop_temp_spec(&mut self) {
if let Some(previous_spec) = self.spec_stack.pop() {
self.set_new_spec(previous_spec);
}
}
pub fn flush(&self) {
self.primary_writer.flush().ok();
for writer in self.other_writers.values() {
writer.flush().ok();
}
}
pub fn reset_flw(&self, flwb: &FileLogWriterBuilder) -> Result<(), FlexiLoggerError> {
if let PrimaryWriter::Multi(ref mw) = &*self.primary_writer {
mw.reset_file_log_writer(flwb)
} else {
Err(FlexiLoggerError::Reset)
}
}
pub fn shutdown(&self) {
self.primary_writer.shutdown();
for writer in self.other_writers.values() {
writer.shutdown();
}
}
#[doc(hidden)]
pub fn validate_logs(&self, expected: &[(&'static str, &'static str, &'static str)]) {
self.primary_writer.validate_logs(expected);
}
}
impl Drop for LoggerHandle {
fn drop(&mut self) {
self.primary_writer.shutdown();
for writer in self.other_writers.values() {
writer.shutdown();
}
}
}
#[cfg(feature = "specfile_without_notification")]
pub trait LogSpecSubscriber: 'static + Send {
fn set_new_spec(&mut self, new_spec: LogSpecification) -> Result<(), FlexiLoggerError>;
fn initial_spec(&self) -> Result<LogSpecification, FlexiLoggerError>;
}
#[cfg(feature = "specfile_without_notification")]
impl LogSpecSubscriber for LoggerHandle {
fn set_new_spec(&mut self, new_spec: LogSpecification) -> Result<(), FlexiLoggerError> {
let max_level = new_spec.max_level();
self.spec
.write()
.map_err(|_| FlexiLoggerError::Poison)?
.update_from(new_spec);
self.reconfigure(max_level);
Ok(())
}
fn initial_spec(&self) -> Result<LogSpecification, FlexiLoggerError> {
Ok((*self.spec.read().map_err(|_e| FlexiLoggerError::Poison)?).clone())
}
}