use crate::{
filter::LevelFilter,
layer::{Context, Layer},
};
use core::{any::type_name, fmt, marker::PhantomData};
use tracing_core::{Interest, Metadata, Subscriber};
#[derive(Clone)]
pub struct FilterFn<F = fn(&Metadata<'_>) -> bool> {
enabled: F,
max_level_hint: Option<LevelFilter>,
}
pub struct DynFilterFn<
S,
F = fn(&Metadata<'_>, &Context<'_, S>) -> bool,
R = fn(&'static Metadata<'static>) -> Interest,
> {
enabled: F,
register_callsite: Option<R>,
max_level_hint: Option<LevelFilter>,
_s: PhantomData<fn(S)>,
}
pub fn filter_fn<F>(f: F) -> FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
FilterFn::new(f)
}
pub fn dynamic_filter_fn<S, F>(f: F) -> DynFilterFn<S, F>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
DynFilterFn::new(f)
}
impl<F> FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
pub fn new(enabled: F) -> Self {
Self {
enabled,
max_level_hint: None,
}
}
pub fn with_max_level_hint(self, max_level_hint: impl Into<LevelFilter>) -> Self {
Self {
max_level_hint: Some(max_level_hint.into()),
..self
}
}
#[inline]
pub(in crate::filter) fn is_enabled(&self, metadata: &Metadata<'_>) -> bool {
let enabled = (self.enabled)(metadata);
debug_assert!(
!enabled || self.is_below_max_level(metadata),
"FilterFn<{}> claimed it would only enable {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
enabled
}
#[inline]
pub(in crate::filter) fn is_callsite_enabled(
&self,
metadata: &'static Metadata<'static>,
) -> Interest {
if (self.enabled)(metadata) {
debug_assert!(
self.is_below_max_level(metadata),
"FilterFn<{}> claimed it was only interested in {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
return Interest::always();
}
Interest::never()
}
fn is_below_max_level(&self, metadata: &Metadata<'_>) -> bool {
self.max_level_hint
.as_ref()
.map(|hint| metadata.level() <= hint)
.unwrap_or(true)
}
}
impl<S, F> Layer<S> for FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool + 'static,
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
self.is_enabled(metadata)
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
impl<F> From<F> for FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
fn from(enabled: F) -> Self {
Self::new(enabled)
}
}
impl<F> fmt::Debug for FilterFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FilterFn")
.field("enabled", &format_args!("{}", type_name::<F>()))
.field("max_level_hint", &self.max_level_hint)
.finish()
}
}
impl<S, F> DynFilterFn<S, F>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
pub fn new(enabled: F) -> Self {
Self {
enabled,
register_callsite: None,
max_level_hint: None,
_s: PhantomData,
}
}
}
impl<S, F, R> DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
pub fn with_max_level_hint(self, max_level_hint: impl Into<LevelFilter>) -> Self {
Self {
max_level_hint: Some(max_level_hint.into()),
..self
}
}
pub fn with_callsite_filter<R2>(self, callsite_enabled: R2) -> DynFilterFn<S, F, R2>
where
R2: Fn(&'static Metadata<'static>) -> Interest,
{
let register_callsite = Some(callsite_enabled);
let DynFilterFn {
enabled,
max_level_hint,
_s,
..
} = self;
DynFilterFn {
enabled,
register_callsite,
max_level_hint,
_s,
}
}
fn default_callsite_enabled(&self, metadata: &Metadata<'_>) -> Interest {
if !is_below_max_level(&self.max_level_hint, metadata) {
debug_assert!(
!(self.enabled)(metadata, &Context::none()),
"DynFilterFn<{}> claimed it would only enable {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
return Interest::never();
}
Interest::sometimes()
}
}
impl<S, F, R> DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
R: Fn(&'static Metadata<'static>) -> Interest,
{
#[inline]
fn is_enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
let enabled = (self.enabled)(metadata, cx);
debug_assert!(
!enabled || is_below_max_level(&self.max_level_hint, metadata),
"DynFilterFn<{}> claimed it would only enable {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
enabled
}
#[inline]
fn is_callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
let interest = self
.register_callsite
.as_ref()
.map(|callsite_enabled| callsite_enabled(metadata))
.unwrap_or_else(|| self.default_callsite_enabled(metadata));
debug_assert!(
interest.is_never() || is_below_max_level(&self.max_level_hint, metadata),
"DynFilterFn<{}, {}> claimed it was only interested in {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
type_name::<R>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
interest
}
}
impl<S, F, R> Layer<S> for DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool + 'static,
R: Fn(&'static Metadata<'static>) -> Interest + 'static,
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool {
self.is_enabled(metadata, &cx)
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
impl<S, F, R> fmt::Debug for DynFilterFn<S, F, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("DynFilterFn");
s.field("enabled", &format_args!("{}", type_name::<F>()));
if self.register_callsite.is_some() {
s.field(
"register_callsite",
&format_args!("Some({})", type_name::<R>()),
);
} else {
s.field("register_callsite", &format_args!("None"));
}
s.field("max_level_hint", &self.max_level_hint).finish()
}
}
impl<S, F, R> Clone for DynFilterFn<S, F, R>
where
F: Clone,
R: Clone,
{
fn clone(&self) -> Self {
Self {
enabled: self.enabled.clone(),
register_callsite: self.register_callsite.clone(),
max_level_hint: self.max_level_hint,
_s: PhantomData,
}
}
}
impl<F, S> From<F> for DynFilterFn<S, F>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
fn from(f: F) -> Self {
Self::new(f)
}
}
feature! {
#![all(feature = "registry", feature = "std")]
use crate::layer::Filter;
impl<S, F> Filter<S> for FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, S>) -> bool {
self.is_enabled(metadata)
}
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
impl<S, F, R> Filter<S> for DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
R: Fn(&'static Metadata<'static>) -> Interest,
{
fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
self.is_enabled(metadata, cx)
}
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
}
fn is_below_max_level(hint: &Option<LevelFilter>, metadata: &Metadata<'_>) -> bool {
hint.as_ref()
.map(|hint| metadata.level() <= hint)
.unwrap_or(true)
}