use tracing_core::{field::FieldSet, span::Id, Metadata};
feature! {
#![feature = "std"]
mod extensions;
pub use extensions::{Extensions, ExtensionsMut};
}
feature! {
#![all(feature = "registry", feature = "std")]
mod sharded;
mod stack;
pub use sharded::Data;
pub use sharded::Registry;
use crate::filter::FilterId;
}
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<'a, 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>;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn extensions(&self) -> Extensions<'_>;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
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(all(feature = "registry", feature = "std"))]
filter: FilterId,
}
feature! {
#![any(feature = "alloc", feature = "std")]
#[cfg(not(feature = "smallvec"))]
use alloc::vec::{self, Vec};
use core::{fmt,iter};
pub struct ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
#[cfg(feature = "smallvec")]
spans: iter::Rev<smallvec::IntoIter<SpanRefVecArray<'a, R>>>,
#[cfg(not(feature = "smallvec"))]
spans: iter::Rev<vec::IntoIter<SpanRef<'a, R>>>,
}
#[cfg(feature = "smallvec")]
type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16];
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 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> fmt::Debug for ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("ScopeFromRoot { .. }")
}
}
}
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(all(feature = "registry", feature = "std"))]
let curr = curr.with_filter(self.filter);
self.next = curr.data.parent().cloned();
#[cfg(all(feature = "registry", feature = "std"))]
{
if !curr.is_enabled_for(self.filter) {
continue;
}
}
return Some(curr);
}
}
}
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()
}
pub fn parent(&self) -> Option<Self> {
let id = self.data.parent()?;
let data = self.registry.span_data(id)?;
#[cfg(all(feature = "registry", feature = "std"))]
{
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(all(feature = "registry", feature = "std")))]
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,
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn extensions(&self) -> Extensions<'_> {
self.data.extensions()
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn extensions_mut(&self) -> ExtensionsMut<'_> {
self.data.extensions_mut()
}
#[cfg(all(feature = "registry", feature = "std"))]
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(all(feature = "registry", feature = "std"))]
pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool {
self.data.is_enabled_for(filter)
}
#[inline]
#[cfg(all(feature = "registry", feature = "std"))]
fn with_filter(self, filter: FilterId) -> Self {
Self { filter, ..self }
}
}
#[cfg(all(test, feature = "registry", feature = "std"))]
mod tests {
use crate::{
layer::{Context, Layer},
prelude::*,
registry::LookupSpan,
};
use std::{
sync::{Arc, Mutex},
vec::Vec,
};
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"]
);
}
}