use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, Event};
use crate::registry::{self, LookupSpan, SpanRef};
#[cfg(feature = "registry")]
use crate::{filter::FilterId, registry::Registry};
#[derive(Debug)]
pub struct Context<'a, S> {
subscriber: Option<&'a S>,
#[cfg(feature = "registry")]
filter: FilterId,
}
#[deprecated(note = "renamed to crate::registry::ScopeFromRoot", since = "0.2.19")]
#[derive(Debug)]
pub struct Scope<'a, L>(std::iter::Flatten<std::option::IntoIter<registry::ScopeFromRoot<'a, L>>>)
where
L: LookupSpan<'a>;
#[allow(deprecated)]
impl<'a, L> Iterator for Scope<'a, L>
where
L: LookupSpan<'a>,
{
type Item = SpanRef<'a, L>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
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(feature = "registry")]
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(feature = "registry")]
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(feature = "registry")]
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))
}
#[deprecated(
note = "equivalent to `self.current_span().id().and_then(|id| self.span_scope(id).from_root())` but consider passing an explicit ID instead of relying on the contextual span",
since = "0.2.19"
)]
#[allow(deprecated)]
pub fn scope(&self) -> Scope<'_, S>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Scope(
self.lookup_current()
.as_ref()
.map(registry::SpanRef::scope)
.map(registry::Scope::from_root)
.into_iter()
.flatten(),
)
}
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(feature = "registry")]
pub(crate) fn with_filter(self, filter: FilterId) -> Self {
let filter = self.filter.and(filter);
Self { filter, ..self }
}
#[cfg(feature = "registry")]
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(feature = "registry")]
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(feature = "registry")]
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(feature = "registry")]
filter: self.filter,
}
}
}