#![doc(html_root_url = "https://docs.rs/tracing-capture/0.1.0")]
#![warn(missing_debug_implementations, missing_docs, bare_trait_objects)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::must_use_candidate, clippy::module_name_repetitions)]
use tracing_core::Metadata;
use std::{cmp, ops, ptr};
mod iter;
mod layer;
pub mod predicates;
pub use crate::{
iter::{CapturedEvents, CapturedSpans, DescendantEvents, DescendantSpans},
layer::{CaptureLayer, SharedStorage, Storage},
};
use tracing_tunnel::{TracedValue, TracedValues};
mod sealed {
pub trait Sealed {}
}
#[derive(Debug)]
struct CapturedEventInner {
metadata: &'static Metadata<'static>,
values: TracedValues<&'static str>,
id: CapturedEventId,
parent_id: Option<CapturedSpanId>,
}
type CapturedEventId = id_arena::Id<CapturedEventInner>;
#[derive(Debug, Clone, Copy)]
pub struct CapturedEvent<'a> {
inner: &'a CapturedEventInner,
storage: &'a Storage,
}
impl<'a> CapturedEvent<'a> {
pub fn metadata(&self) -> &'static Metadata<'static> {
self.inner.metadata
}
pub fn values(&self) -> impl Iterator<Item = (&'a str, &'a TracedValue)> + 'a {
self.inner.values.iter()
}
pub fn value(&self, name: &str) -> Option<&'a TracedValue> {
self.inner.values.get(name)
}
pub fn message(&self) -> Option<&'a str> {
self.value("message").and_then(|message| match message {
TracedValue::Object(obj) => Some(obj.as_ref()),
TracedValue::String(s) => Some(s),
TracedValue::Error(err) => Some(&err.message),
_ => None,
})
}
pub fn parent(&self) -> Option<CapturedSpan<'a>> {
self.inner.parent_id.map(|id| self.storage.span(id))
}
pub fn ancestors(&self) -> impl Iterator<Item = CapturedSpan<'a>> + '_ {
std::iter::successors(self.parent(), CapturedSpan::parent)
}
}
impl PartialEq for CapturedEvent<'_> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.storage, other.storage) && self.inner.id == other.inner.id
}
}
impl Eq for CapturedEvent<'_> {}
impl PartialOrd for CapturedEvent<'_> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
if ptr::eq(self.storage, other.storage) {
Some(self.inner.id.cmp(&other.inner.id))
} else {
None
}
}
}
impl ops::Index<&str> for CapturedEvent<'_> {
type Output = TracedValue;
fn index(&self, index: &str) -> &Self::Output {
self.value(index)
.unwrap_or_else(|| panic!("field `{index}` is not contained in event"))
}
}
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct SpanStats {
pub entered: usize,
pub exited: usize,
pub is_closed: bool,
}
#[derive(Debug)]
struct CapturedSpanInner {
metadata: &'static Metadata<'static>,
values: TracedValues<&'static str>,
stats: SpanStats,
id: CapturedSpanId,
parent_id: Option<CapturedSpanId>,
child_ids: Vec<CapturedSpanId>,
event_ids: Vec<CapturedEventId>,
}
type CapturedSpanId = id_arena::Id<CapturedSpanInner>;
#[derive(Debug, Clone, Copy)]
pub struct CapturedSpan<'a> {
inner: &'a CapturedSpanInner,
storage: &'a Storage,
}
impl<'a> CapturedSpan<'a> {
pub fn metadata(&self) -> &'static Metadata<'static> {
self.inner.metadata
}
pub fn values(&self) -> impl Iterator<Item = (&'a str, &'a TracedValue)> + 'a {
self.inner.values.iter()
}
pub fn value(&self, name: &str) -> Option<&'a TracedValue> {
self.inner.values.get(name)
}
pub fn stats(&self) -> SpanStats {
self.inner.stats
}
pub fn events(&self) -> CapturedEvents<'a> {
CapturedEvents::from_slice(self.storage, &self.inner.event_ids)
}
pub fn parent(&self) -> Option<Self> {
self.inner.parent_id.map(|id| self.storage.span(id))
}
pub fn ancestors(&self) -> impl Iterator<Item = CapturedSpan<'a>> + '_ {
std::iter::successors(self.parent(), Self::parent)
}
pub fn children(&self) -> CapturedSpans<'a> {
CapturedSpans::from_slice(self.storage, &self.inner.child_ids)
}
pub fn descendants(&self) -> DescendantSpans<'a> {
DescendantSpans::new(self)
}
pub fn descendant_events(&self) -> DescendantEvents<'a> {
DescendantEvents::new(self)
}
}
impl PartialEq for CapturedSpan<'_> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.storage, other.storage) && self.inner.id == other.inner.id
}
}
impl Eq for CapturedSpan<'_> {}
impl PartialOrd for CapturedSpan<'_> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
if ptr::eq(self.storage, other.storage) {
Some(self.inner.id.cmp(&other.inner.id))
} else {
None
}
}
}
impl ops::Index<&str> for CapturedSpan<'_> {
type Output = TracedValue;
fn index(&self, index: &str) -> &Self::Output {
self.value(index)
.unwrap_or_else(|| panic!("field `{index}` is not contained in span"))
}
}
pub trait Captured<'a>: Eq + PartialOrd + sealed::Sealed {
fn metadata(&self) -> &'static Metadata<'static>;
fn value(&self, name: &str) -> Option<&'a TracedValue>;
fn parent(&self) -> Option<CapturedSpan<'a>>;
}
impl sealed::Sealed for CapturedSpan<'_> {}
impl<'a> Captured<'a> for CapturedSpan<'a> {
#[inline]
fn metadata(&self) -> &'static Metadata<'static> {
self.metadata()
}
#[inline]
fn value(&self, name: &str) -> Option<&'a TracedValue> {
self.value(name)
}
#[inline]
fn parent(&self) -> Option<CapturedSpan<'a>> {
self.parent()
}
}
impl sealed::Sealed for CapturedEvent<'_> {}
impl<'a> Captured<'a> for CapturedEvent<'a> {
#[inline]
fn metadata(&self) -> &'static Metadata<'static> {
self.metadata()
}
#[inline]
fn value(&self, name: &str) -> Option<&'a TracedValue> {
self.value(name)
}
#[inline]
fn parent(&self) -> Option<CapturedSpan<'a>> {
self.parent()
}
}
#[cfg(doctest)]
doc_comment::doctest!("../README.md");