use std::fmt::Debug;
#[cfg(feature = "registry")]
use crate::filter::FilterId;
use tracing_core::{field::FieldSet, span::Id, Metadata};
mod extensions;
#[cfg(feature = "registry")]
mod sharded;
#[cfg(feature = "registry")]
mod stack;
pub use extensions::{Extensions, ExtensionsMut};
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
pub use sharded::Data;
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
pub use sharded::Registry;
pub trait LookupSpan<'a> {
type Data: SpanData<'a>;
fn span_data(&'a self, id: &Id) -> Option<Self::Data>;
fn span(&'a self, id: &Id) -> Option<SpanRef<'_, Self>>
where
Self: Sized,
{
let data = self.span_data(id)?;
Some(SpanRef {
registry: self,
data,
#[cfg(feature = "registry")]
filter: FilterId::none(),
})
}
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
fn register_filter(&mut self) -> FilterId {
panic!(
"{} does not currently support filters",
std::any::type_name::<Self>()
)
}
}
pub trait SpanData<'a> {
fn id(&self) -> Id;
fn metadata(&self) -> &'static Metadata<'static>;
fn parent(&self) -> Option<&Id>;
fn extensions(&self) -> Extensions<'_>;
fn extensions_mut(&self) -> ExtensionsMut<'_>;
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
fn is_enabled_for(&self, filter: FilterId) -> bool {
let _ = filter;
true
}
}
#[derive(Debug)]
pub struct SpanRef<'a, R: LookupSpan<'a>> {
registry: &'a R,
data: R::Data,
#[cfg(feature = "registry")]
filter: FilterId,
}
#[derive(Debug)]
pub struct Scope<'a, R> {
registry: &'a R,
next: Option<Id>,
#[cfg(feature = "registry")]
filter: FilterId,
}
impl<'a, R> Scope<'a, R>
where
R: LookupSpan<'a>,
{
#[allow(clippy::wrong_self_convention)]
pub fn from_root(self) -> ScopeFromRoot<'a, R> {
#[cfg(feature = "smallvec")]
type Buf<T> = smallvec::SmallVec<T>;
#[cfg(not(feature = "smallvec"))]
type Buf<T> = Vec<T>;
ScopeFromRoot {
spans: self.collect::<Buf<_>>().into_iter().rev(),
}
}
}
impl<'a, R> Iterator for Scope<'a, R>
where
R: LookupSpan<'a>,
{
type Item = SpanRef<'a, R>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let curr = self.registry.span(self.next.as_ref()?)?;
#[cfg(feature = "registry")]
let curr = curr.with_filter(self.filter);
self.next = curr.data.parent().cloned();
#[cfg(feature = "registry")]
if !curr.is_enabled_for(self.filter) {
continue;
}
return Some(curr);
}
}
}
pub struct ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
#[cfg(feature = "smallvec")]
spans: std::iter::Rev<smallvec::IntoIter<SpanRefVecArray<'a, R>>>,
#[cfg(not(feature = "smallvec"))]
spans: std::iter::Rev<std::vec::IntoIter<SpanRef<'a, R>>>,
}
impl<'a, R> Iterator for ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
type Item = SpanRef<'a, R>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.spans.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.spans.size_hint()
}
}
impl<'a, R> Debug for ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad("ScopeFromRoot { .. }")
}
}
#[deprecated(note = "replaced by `Scope`")]
#[derive(Debug)]
pub struct Parents<'a, R>(Scope<'a, R>);
#[allow(deprecated)]
impl<'a, R> Iterator for Parents<'a, R>
where
R: LookupSpan<'a>,
{
type Item = SpanRef<'a, R>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[deprecated(note = "replaced by `ScopeFromRoot`", since = "0.2.19")]
#[derive(Debug)]
pub struct FromRoot<'a, R>(ScopeFromRoot<'a, R>)
where
R: LookupSpan<'a>;
#[allow(deprecated)]
impl<'a, R> Iterator for FromRoot<'a, R>
where
R: LookupSpan<'a>,
{
type Item = SpanRef<'a, R>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[cfg(feature = "smallvec")]
type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16];
impl<'a, R> SpanRef<'a, R>
where
R: LookupSpan<'a>,
{
pub fn id(&self) -> Id {
self.data.id()
}
pub fn metadata(&self) -> &'static Metadata<'static> {
self.data.metadata()
}
pub fn name(&self) -> &'static str {
self.data.metadata().name()
}
pub fn fields(&self) -> &FieldSet {
self.data.metadata().fields()
}
#[deprecated(
note = "this method cannot properly support per-layer filtering, and may \
return the `Id` of a disabled span if per-layer filtering is in \
use. use `.parent().map(SpanRef::id)` instead.",
since = "0.2.21"
)]
pub fn parent_id(&self) -> Option<&Id> {
self.data.parent()
}
pub fn parent(&self) -> Option<Self> {
let id = self.data.parent()?;
let data = self.registry.span_data(id)?;
#[cfg(feature = "registry")]
{
let mut data = data;
loop {
if data.is_enabled_for(self.filter) {
return Some(Self {
registry: self.registry,
filter: self.filter,
data,
});
}
let id = data.parent()?;
data = self.registry.span_data(id)?;
}
}
#[cfg(not(feature = "registry"))]
Some(Self {
registry: self.registry,
data,
})
}
pub fn scope(&self) -> Scope<'a, R> {
Scope {
registry: self.registry,
next: Some(self.id()),
#[cfg(feature = "registry")]
filter: self.filter,
}
}
#[deprecated(
note = "equivalent to `self.parent().into_iter().flat_map(SpanRef::scope)`, but consider whether excluding `self` is actually intended"
)]
#[allow(deprecated)]
pub fn parents(&self) -> Parents<'a, R> {
Parents(Scope {
registry: self.registry,
next: self.parent_id().cloned(),
#[cfg(feature = "registry")]
filter: self.filter,
})
}
#[deprecated(
note = "equivalent to `self.parent().into_iter().flat_map(|span| span.scope().from_root())`, but consider whether excluding `self` is actually intended",
since = "0.2.19"
)]
#[allow(deprecated)]
pub fn from_root(&self) -> FromRoot<'a, R> {
FromRoot(self.parents().0.from_root())
}
pub fn extensions(&self) -> Extensions<'_> {
self.data.extensions()
}
pub fn extensions_mut(&self) -> ExtensionsMut<'_> {
self.data.extensions_mut()
}
#[cfg(feature = "registry")]
pub(crate) fn try_with_filter(self, filter: FilterId) -> Option<Self> {
if self.is_enabled_for(filter) {
return Some(self.with_filter(filter));
}
None
}
#[inline]
#[cfg(feature = "registry")]
pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool {
self.data.is_enabled_for(filter)
}
#[inline]
#[cfg(feature = "registry")]
fn with_filter(self, filter: FilterId) -> Self {
Self { filter, ..self }
}
}
#[cfg(all(test, feature = "registry"))]
mod tests {
use crate::{
layer::{Context, Layer},
prelude::*,
registry::LookupSpan,
};
use std::sync::{Arc, Mutex};
use tracing::{span, Subscriber};
#[test]
fn spanref_scope_iteration_order() {
let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
#[derive(Default)]
struct PrintingLayer {
last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
}
impl<S> Layer<S> for PrintingLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
let span = ctx.span(id).unwrap();
let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
*self.last_entered_scope.lock().unwrap() = scope;
}
}
let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer {
last_entered_scope: last_entered_scope.clone(),
}));
let _root = tracing::info_span!("root").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
let _child = tracing::info_span!("child").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["child", "root"]);
let _leaf = tracing::info_span!("leaf").entered();
assert_eq!(
&*last_entered_scope.lock().unwrap(),
&["leaf", "child", "root"]
);
}
#[test]
fn spanref_scope_fromroot_iteration_order() {
let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
#[derive(Default)]
struct PrintingLayer {
last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
}
impl<S> Layer<S> for PrintingLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
let span = ctx.span(id).unwrap();
let scope = span
.scope()
.from_root()
.map(|span| span.name())
.collect::<Vec<_>>();
*self.last_entered_scope.lock().unwrap() = scope;
}
}
let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer {
last_entered_scope: last_entered_scope.clone(),
}));
let _root = tracing::info_span!("root").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
let _child = tracing::info_span!("child").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["root", "child",]);
let _leaf = tracing::info_span!("leaf").entered();
assert_eq!(
&*last_entered_scope.lock().unwrap(),
&["root", "child", "leaf"]
);
}
}