use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, Event};
use crate::registry::{self, LookupSpan, SpanRef};
#[cfg(all(feature = "registry", feature = "std"))]
use crate::{filter::FilterId, registry::Registry};
#[derive(Debug)]
pub struct Context<'a, S> {
subscriber: Option<&'a S>,
#[cfg(all(feature = "registry", feature = "std"))]
filter: FilterId,
}
impl<'a, S> Context<'a, S>
where
S: Subscriber,
{
pub(super) fn new(subscriber: &'a S) -> Self {
Self {
subscriber: Some(subscriber),
#[cfg(feature = "registry")]
filter: FilterId::none(),
}
}
#[inline]
pub fn current_span(&self) -> span::Current {
self.subscriber
.map(Subscriber::current_span)
.unwrap_or_else(span::Current::none)
}
#[inline]
pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
self.subscriber
.map(|subscriber| subscriber.enabled(metadata))
.unwrap_or(true)
}
#[inline]
pub fn event(&self, event: &Event<'_>) {
if let Some(subscriber) = self.subscriber {
subscriber.event(event);
}
}
#[inline]
pub fn event_span(&self, event: &Event<'_>) -> Option<SpanRef<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
if event.is_root() {
None
} else if event.is_contextual() {
self.lookup_current()
} else {
event.parent().and_then(|id| self.span(id))
}
}
#[inline]
pub fn metadata(&self, id: &span::Id) -> Option<&'static Metadata<'static>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
let span = self.span(id)?;
Some(span.metadata())
}
#[inline]
pub fn span(&self, id: &span::Id) -> Option<registry::SpanRef<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
let span = self.subscriber.as_ref()?.span(id)?;
#[cfg(all(feature = "registry", feature = "std"))]
return span.try_with_filter(self.filter);
#[cfg(not(feature = "registry"))]
Some(span)
}
#[inline]
pub fn exists(&self, id: &span::Id) -> bool
where
S: for<'lookup> LookupSpan<'lookup>,
{
self.subscriber.as_ref().and_then(|s| s.span(id)).is_some()
}
#[inline]
pub fn lookup_current(&self) -> Option<registry::SpanRef<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
let subscriber = *self.subscriber.as_ref()?;
let current = subscriber.current_span();
let id = current.id()?;
let span = subscriber.span(id);
debug_assert!(
span.is_some(),
"the subscriber should have data for the current span ({:?})!",
id,
);
#[cfg(all(feature = "registry", feature = "std"))]
{
if let Some(span) = span?.try_with_filter(self.filter) {
Some(span)
} else {
self.lookup_current_filtered(subscriber)
}
}
#[cfg(not(feature = "registry"))]
span
}
#[inline(never)]
#[cfg(all(feature = "registry", feature = "std"))]
fn lookup_current_filtered<'lookup>(
&self,
subscriber: &'lookup S,
) -> Option<registry::SpanRef<'lookup, S>>
where
S: LookupSpan<'lookup>,
{
let registry = (subscriber as &dyn Subscriber).downcast_ref::<Registry>()?;
registry
.span_stack()
.iter()
.find_map(|id| subscriber.span(id)?.try_with_filter(self.filter))
}
pub fn span_scope(&self, id: &span::Id) -> Option<registry::Scope<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Some(self.span(id)?.scope())
}
pub fn event_scope(&self, event: &Event<'_>) -> Option<registry::Scope<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Some(self.event_span(event)?.scope())
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn with_filter(self, filter: FilterId) -> Self {
let filter = self.filter.and(filter);
Self { filter, ..self }
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn is_enabled_for(&self, span: &span::Id, filter: FilterId) -> bool
where
S: for<'lookup> LookupSpan<'lookup>,
{
self.is_enabled_inner(span, filter).unwrap_or(false)
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn if_enabled_for(self, span: &span::Id, filter: FilterId) -> Option<Self>
where
S: for<'lookup> LookupSpan<'lookup>,
{
if self.is_enabled_inner(span, filter)? {
Some(self.with_filter(filter))
} else {
None
}
}
#[cfg(all(feature = "registry", feature = "std"))]
fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option<bool>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Some(self.span(span)?.is_enabled_for(filter))
}
}
impl<'a, S> Context<'a, S> {
pub(crate) fn none() -> Self {
Self {
subscriber: None,
#[cfg(feature = "registry")]
filter: FilterId::none(),
}
}
}
impl<'a, S> Clone for Context<'a, S> {
#[inline]
fn clone(&self) -> Self {
let subscriber = self.subscriber.as_ref().copied();
Context {
subscriber,
#[cfg(all(feature = "registry", feature = "std"))]
filter: self.filter,
}
}
}