use std::sync::{Arc, RwLock};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::Layered;
use crate::config::{FilterDirective, LogFilter, LogLevel};
use crate::error::{ActaError, Result};
use crate::fmt::{Icons, LevelLabels, StyleConfig, Theme};
pub(crate) type FmtLayer =
Box<dyn tracing_subscriber::Layer<tracing_subscriber::Registry> + Send + Sync>;
pub(crate) type InnerSubscriber = Layered<FmtLayer, tracing_subscriber::Registry>;
type RawReloadHandle = tracing_subscriber::reload::Handle<EnvFilter, InnerSubscriber>;
#[must_use = "dropping ReloadHandle loses the ability to change log filters at runtime"]
#[derive(Clone)]
pub struct ReloadHandle {
raw: RawReloadHandle,
filter: Arc<RwLock<LogFilter>>,
style: Option<StyleConfig>,
}
impl std::fmt::Debug for ReloadHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReloadHandle").finish_non_exhaustive()
}
}
impl ReloadHandle {
pub fn reload(&self, directive: &str) -> Result<()> {
self.apply_directive(directive)?;
self.store_filter(LogFilter::new(LogLevel::Custom(FilterDirective::new(
directive,
))))?;
Ok(())
}
pub fn set_filter(&self, filter: LogFilter) -> Result<()> {
let directive = filter.as_filter_directive();
self.apply_directive(&directive)?;
self.store_filter(filter)?;
Ok(())
}
pub fn set_level(&self, level: LogLevel) -> Result<()> {
self.update_filter(|filter| filter.level = level)
}
pub fn set_target_level(&self, target: impl Into<String>, level: LogLevel) -> Result<()> {
let target = target.into();
self.update_filter(|filter| filter.set_target_level(target, level))
}
pub fn remove_target_level(&self, target: &str) -> Result<()> {
self.update_filter(|filter| {
filter.remove_target_level(target);
})
}
fn with_style(&mut self, f: impl FnOnce(&mut StyleConfig)) -> Result<()> {
if let Some(ref mut config) = self.style {
f(config);
Ok(())
} else {
Err(ActaError::StyleNotConfigured)
}
}
pub fn set_icons(&mut self, icons: Icons) -> Result<()> {
self.with_style(|s| s.icons = icons)
}
pub fn set_theme(&mut self, theme: Theme) -> Result<()> {
self.with_style(|s| s.theme = theme)
}
pub fn set_labels(&mut self, labels: LevelLabels) -> Result<()> {
self.with_style(|s| s.labels = labels)
}
fn update_filter(&self, update: impl FnOnce(&mut LogFilter)) -> Result<()> {
let mut next = self.current_filter()?;
update(&mut next);
self.set_filter(next)
}
fn current_filter(&self) -> Result<LogFilter> {
Ok(self
.filter
.read()
.map_err(|_| ActaError::LockPoisoned)?
.clone())
}
fn store_filter(&self, filter: LogFilter) -> Result<()> {
*self.filter.write().map_err(|_| ActaError::LockPoisoned)? = filter;
Ok(())
}
fn apply_directive(&self, directive: &str) -> Result<()> {
let filter = EnvFilter::try_new(directive)?;
self.raw.modify(|f| *f = filter)?;
Ok(())
}
}
pub fn build_reload_filter(
level: &LogLevel,
style: Option<StyleConfig>,
) -> (
tracing_subscriber::reload::Layer<EnvFilter, InnerSubscriber>,
ReloadHandle,
) {
let filter = EnvFilter::new(level.as_filter_directive());
let (layer, raw_handle) = tracing_subscriber::reload::Layer::new(filter);
(
layer,
ReloadHandle {
raw: raw_handle,
filter: Arc::new(RwLock::new(LogFilter::new(level.clone()))),
style,
},
)
}