use std::sync::Arc;
#[cfg(feature = "tracing-log")]
use std::sync::OnceLock;
use arc_swap::ArcSwap;
use fxhash::FxHashMap;
use tracing::{metadata::LevelFilter, subscriber::Interest, Metadata, Subscriber};
use tracing_subscriber::{filter::Targets, layer};
use elfo_core::{logging::CheckResult, scope};
use crate::{config::LoggingTargetConfig, stats};
#[derive(PartialEq)]
struct ScopeFilterConfig {
targets: Targets,
}
impl Default for ScopeFilterConfig {
fn default() -> Self {
Self {
targets: Targets::new().with_default(LevelFilter::TRACE),
}
}
}
struct Inner {
config: ArcSwap<ScopeFilterConfig>,
#[cfg(feature = "tracing-log")]
log_metadata_name: OnceLock<&'static str>,
}
pub struct ScopeFilter {
inner: Arc<Inner>,
}
impl ScopeFilter {
pub(crate) fn new() -> Self {
Self {
inner: Arc::new(Inner {
config: ArcSwap::new(Arc::new(ScopeFilterConfig::default())),
#[cfg(feature = "tracing-log")]
log_metadata_name: OnceLock::new(),
}),
}
}
pub(crate) fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
}
}
pub(crate) fn configure(&self, targets: &FxHashMap<String, LoggingTargetConfig>) {
let targets = Targets::new()
.with_default(LevelFilter::TRACE)
.with_targets(
targets
.iter()
.map(|(target, target_config)| (target, target_config.max_level)),
);
let config = Arc::new(ScopeFilterConfig { targets });
let old_config = self.inner.config.swap(Arc::clone(&config));
if config != old_config {
tracing::callsite::rebuild_interest_cache();
}
}
fn interested(&self, meta: &Metadata<'_>) -> Interest {
let config = self.inner.config.load();
if config.targets.would_enable(meta.target(), meta.level()) {
Interest::sometimes()
} else {
Interest::never()
}
}
fn enabled(&self, meta: &Metadata<'_>) -> bool {
let level = *meta.level();
#[cfg(feature = "tracing-log")]
{
let name = meta.name();
if let Some(&log_name) = self.inner.log_metadata_name.get() {
if std::ptr::eq(log_name, name) {
let config = self.inner.config.load();
if !config.targets.would_enable(meta.target(), meta.level()) {
return false;
}
}
} else if name == "log record" {
let config = self.inner.config.load();
if !config.targets.would_enable(meta.target(), meta.level()) {
return false;
}
if meta.target() == "elfo_logger" {
self.inner.log_metadata_name.set(name).ok();
return false;
}
}
}
scope::try_with(|scope| {
if !scope.permissions().is_logging_enabled(level) {
return false;
}
match scope.logging().check(meta) {
CheckResult::Passed => true,
CheckResult::NotInterested => false,
CheckResult::Limited => {
stats::counter_per_level("elfo_limited_events_total", level);
false
}
}
})
.unwrap_or(level <= LevelFilter::INFO)
}
}
impl<S: Subscriber> layer::Layer<S> for ScopeFilter {
fn register_callsite(&self, meta: &'static Metadata<'static>) -> Interest {
self.interested(meta)
}
fn enabled(&self, meta: &Metadata<'_>, _cx: layer::Context<'_, S>) -> bool {
self.enabled(meta)
}
}
impl<S> layer::Filter<S> for ScopeFilter {
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
self.interested(meta)
}
fn enabled(&self, meta: &Metadata<'_>, _cx: &layer::Context<'_, S>) -> bool {
!self.interested(meta).is_never() && self.enabled(meta)
}
}