use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use tracing_core::span::{Attributes, Id};
use tracing_core::subscriber::Subscriber;
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::ExtensionsMut;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::registry::SpanData;
mod channel;
pub use channel::Updates;
pub mod data;
pub use data::Consequences;
use data::Listeners;
pub(crate) type Metadata = &'static tracing_core::Metadata<'static>;
#[derive(Debug, Clone)]
pub struct Trace<M = crate::Metadata>
where
M: Clone + Debug,
{
root: Span<M>,
adj: HashMap<Span<M>, Consequences<M>>,
}
#[derive(Debug, Clone)]
pub struct Span<M = crate::Metadata>
where
M: Clone + Debug,
{
pub id: Id,
pub metadata: M,
}
impl<M> Hash for Span<M>
where
M: Clone + Debug,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl<M> Eq for Span<M> where M: Clone + Debug {}
impl<M, N> PartialEq<Span<N>> for Span<M>
where
M: Clone + Debug,
N: Clone + Debug,
{
fn eq(&self, other: &Span<N>) -> bool {
&self.id == &other.id
}
}
impl<M> Trace<M>
where
M: Clone + Debug,
{
pub fn with_root(root: Span<M>) -> Self {
Self {
root,
adj: Default::default(),
}
}
pub fn root(&self) -> Span<M> {
self.root.to_owned()
}
pub fn root_consequences(&self) -> &Consequences<M> {
self.adj.get(&self.root).unwrap()
}
pub fn consequences(&self, span: &Span<M>) -> Option<&Consequences<M>> {
self.adj.get(span)
}
pub fn apply(mut self, update: Update<M>) -> Option<Trace<M>> {
match update {
Update::OpenDirect { cause, consequence } => {
self.adj
.entry(cause)
.or_insert_with(Consequences::none)
.add_direct(consequence);
Some(self)
}
Update::NewIndirect { cause, consequence } => {
self.adj
.entry(cause)
.or_insert_with(Consequences::none)
.add_indirect(consequence);
Some(self)
}
Update::CloseDirect { span, direct_cause } => {
if let Some(direct_cause) = direct_cause {
let _ = self.adj.remove(&span);
if let Some(consequences) = self.adj.get_mut(&direct_cause) {
consequences.remove_direct(&span);
}
Some(self)
} else {
debug_assert_eq!(self.root, span);
None
}
}
Update::CloseIndirect {
span: id,
indirect_causes,
} => {
let _ = self.adj.remove(&id);
for indirect_cause in indirect_causes {
if let Some(consequences) = self.adj.get_mut(&indirect_cause) {
consequences.remove_direct(&id);
}
}
Some(self)
}
Update::CloseCyclic {
span: id,
direct_cause,
indirect_causes,
} => {
if let Some(direct_cause) = direct_cause {
let _ = self.adj.remove(&id);
if let Some(consequences) = self.adj.get_mut(&direct_cause) {
consequences.remove_direct(&id);
}
for indirect_cause in indirect_causes {
if let Some(consequences) = self.adj.get_mut(&indirect_cause) {
consequences.remove_direct(&id);
}
}
Some(self)
} else {
debug_assert_eq!(self.root, id);
None
}
}
}
}
pub fn iter(&self) -> impl Iterator<Item = (Span<M>, &Consequences<M>)> {
let mut queue = vec![(self.root.clone())];
std::iter::from_fn(move || {
let span = queue.pop()?;
let consequences = self.consequences(&span)?;
queue.extend(consequences.iter_direct());
Some((span, consequences))
})
}
}
fn get_or_init_with<'a, T, F>(extensions: &'a mut ExtensionsMut<'_>, f: F) -> &'a mut T
where
T: 'static + Send + Sync,
F: FnOnce() -> T,
{
if extensions.get_mut::<T>().is_none() {
extensions.insert::<T>(f());
}
extensions.get_mut::<T>().unwrap()
}
pub fn trace<S>(s: &S, id: &Id, update_capacity: usize) -> Option<(Trace, Updates)>
where
S: for<'span> LookupSpan<'span> + ?Sized,
{
let (sender, updates) = channel::bounded(id.clone(), update_capacity);
let root = Span {
id: id.clone(),
metadata: s.span_data(id)?.metadata(),
};
let mut trace = Trace {
root: root.clone(),
adj: HashMap::default(),
};
let mut queue = vec![root.to_owned()];
while let Some(span) = queue.pop() {
if let Some(span_data) = s.span_data(&span.id) {
let mut extensions = span_data.extensions_mut();
get_or_init_with::<Listeners, _>(&mut extensions, Listeners::new)
.insert(sender.clone());
if let Some(consequences) = extensions.get_mut::<Consequences>() {
let direct_consequences = consequences.clone();
queue.extend(direct_consequences.direct.iter().cloned());
trace.adj.insert(span, direct_consequences);
}
} else {
}
}
Some((trace, updates))
}
pub fn consequences<S>(subscriber: &S, span: &Id) -> Option<Consequences>
where
S: for<'span> LookupSpan<'span> + ?Sized,
{
Some(
subscriber
.span_data(span)?
.extensions()
.get::<Consequences>()
.cloned()
.unwrap_or_else(Consequences::default),
)
}
#[derive(Clone, Debug)]
pub enum Update<M = crate::Metadata>
where
M: Clone + Debug,
{
OpenDirect {
cause: Span<M>,
consequence: Span<M>,
},
NewIndirect {
cause: Span<M>,
consequence: Span<M>,
},
CloseDirect {
span: Span<M>,
direct_cause: Option<Span<M>>,
},
CloseIndirect {
span: Span<M>,
indirect_causes: Vec<Span<M>>,
},
CloseCyclic {
span: Span<M>,
direct_cause: Option<Span<M>>,
indirect_causes: Vec<Span<M>>,
},
}
impl<M> Eq for Update<M> where M: Clone + Debug + Eq {}
impl<M> PartialEq<Update<M>> for Update<M>
where
M: Clone + Debug + PartialEq,
{
fn eq(&self, other: &Update<M>) -> bool {
use Update::*;
match (self, other) {
(
OpenDirect {
cause: lhs_cause,
consequence: lhs_consequence,
},
OpenDirect {
cause: rhs_cause,
consequence: rhs_consequence,
},
)
| (
NewIndirect {
cause: lhs_cause,
consequence: lhs_consequence,
},
NewIndirect {
cause: rhs_cause,
consequence: rhs_consequence,
},
) => (lhs_cause == rhs_cause) && (lhs_consequence == rhs_consequence),
(
CloseDirect {
span: lhs_span,
direct_cause: lhs_direct_cause,
},
CloseDirect {
span: rhs_span,
direct_cause: rhs_direct_cause,
},
) => (lhs_span == rhs_span) && (lhs_direct_cause == rhs_direct_cause),
(
CloseIndirect {
span: lhs_span,
indirect_causes: lhs_indirect_causes,
},
CloseIndirect {
span: rhs_span,
indirect_causes: rhs_indirect_causes,
},
) => (lhs_span == rhs_span) && (lhs_indirect_causes == rhs_indirect_causes),
(
CloseCyclic {
span: lhs_span,
direct_cause: lhs_direct_cause,
indirect_causes: lhs_indirect_causes,
},
CloseCyclic {
span: rhs_span,
direct_cause: rhs_direct_cause,
indirect_causes: rhs_indirect_causes,
},
) => {
(lhs_span == rhs_span)
&& (lhs_direct_cause == rhs_direct_cause)
&& (lhs_indirect_causes == rhs_indirect_causes)
}
_ => false,
}
}
}
pub struct Layer;
impl Layer {
pub(crate) fn on_follows_self<S>(&self, span_id: &Id, ctx: Context<'_, S>)
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
use data::IndirectCauses;
let span = ctx.span(span_id).expect("Span not found, this is a bug");
let mut span_data = span.extensions_mut();
let id_and_metadata = Span {
id: span_id.to_owned(),
metadata: span.metadata(),
};
if let Some(consequences) = span_data.get_mut::<Consequences>() {
consequences.indirect.insert(id_and_metadata.clone());
} else {
span_data.insert(Consequences::with_indirect(id_and_metadata.clone()));
}
if let Some(follows_from) = span_data.get_mut::<IndirectCauses>() {
follows_from.add_cause(id_and_metadata.clone());
} else {
span_data.insert(IndirectCauses::with_cause(id_and_metadata.clone()));
}
if let Some(listeners) = span_data.get_mut::<Listeners>() {
channel::Sender::broadcast(
listeners,
Update::NewIndirect {
cause: id_and_metadata.clone(),
consequence: id_and_metadata.clone(),
},
);
}
}
}
impl<S> tracing_subscriber::layer::Layer<S> for Layer
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
fn on_new_span(&self, _: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
let span = ctx.span(id).expect(
"The `id` provided to
`tracing_causality::Layer::on_new_span` did not correspond to an
opened `Span` for the underlying subscriber. This span may have \
already been closed, or been assigned by a different subscriber, \
or there may be a bug in the subscriber underlying this layer.",
);
let consequence_id_and_metadata = Span {
id: id.to_owned(),
metadata: span.metadata(),
};
if let Some(direct_cause) = span.parent() {
let mut cause_extensions = direct_cause.extensions_mut();
let cause_id_and_metadata = Span {
id: direct_cause.id(),
metadata: direct_cause.metadata(),
};
if let Some(listeners) = cause_extensions.get_mut::<Listeners>() {
channel::Sender::broadcast(
listeners,
Update::OpenDirect {
cause: cause_id_and_metadata,
consequence: consequence_id_and_metadata.clone(),
},
);
crate::get_or_init_with::<Listeners, _>(&mut span.extensions_mut(), Listeners::new)
.extend(listeners.iter().cloned());
}
if let Some(consequences) = cause_extensions.get_mut::<Consequences>() {
consequences.direct.insert(consequence_id_and_metadata);
} else {
cause_extensions.insert(Consequences::with_direct(consequence_id_and_metadata));
}
}
}
fn on_follows_from(
&self,
consequence_id_and_metadata: &Id,
cause_id_and_metadata: &Id,
ctx: Context<'_, S>,
) {
use data::IndirectCauses;
if cause_id_and_metadata == consequence_id_and_metadata {
return self.on_follows_self(consequence_id_and_metadata, ctx);
}
let cause = ctx.span(cause_id_and_metadata).expect(
"The `cause_id_and_metadata` provided to
`tracing_causality::Layer::on_follows_from` did not correspond to \
an opened `Span` for the underlying subscriber. This span may have \
already been closed, or been assigned by a different subscriber, \
or there may be a bug in the subscriber underlying this layer.",
);
let consequence = ctx.span(consequence_id_and_metadata).expect(
"The `consequence_id_and_metadata` provided to
`tracing_causality::Layer::on_follows_from` did not correspond to \
an opened `Span` for the underlying subscriber. This span may have \
already been closed, or been assigned by a different subscriber, \
or there may be a bug in the subscriber underlying this layer.",
);
let cause_id_and_metadata = Span {
id: cause_id_and_metadata.to_owned(),
metadata: cause.metadata(),
};
let consequence_id_and_metadata = Span {
id: consequence_id_and_metadata.to_owned(),
metadata: consequence.metadata(),
};
let mut cause_data = cause.extensions_mut();
let mut consequence_data = consequence.extensions_mut();
if let Some(consequences) = cause_data.get_mut::<Consequences>() {
consequences
.indirect
.insert(consequence_id_and_metadata.clone());
} else {
cause_data.insert(Consequences::with_indirect(
consequence_id_and_metadata.clone(),
));
}
if let Some(follows_from) = consequence_data.get_mut::<IndirectCauses>() {
follows_from.add_cause(cause_id_and_metadata.clone());
} else {
consequence_data.insert(IndirectCauses::with_cause(cause_id_and_metadata.clone()));
}
if let Some(listeners) = cause_data.get_mut::<Listeners>() {
channel::Sender::broadcast(
listeners,
Update::NewIndirect {
cause: cause_id_and_metadata,
consequence: consequence_id_and_metadata,
},
);
}
}
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
use data::IndirectCauses;
let span = ctx.span(&id).expect(
"The `id` provided to
`tracing_causality::Layer::close` did not correspond to an opened \
`Span` for the underlying subscriber. This span have \
already been closed, or been assigned by a different subscriber, \
or there may be a bug in the subscriber underlying this layer.",
);
let mut extensions = span.extensions_mut();
let closed_id_and_metadata = Span {
id: id.to_owned(),
metadata: span.metadata(),
};
let mut is_cyclic = None;
if let Some(follows_from) = extensions.remove::<IndirectCauses>() {
let indirect_causes: Vec<Span> = follows_from.causes.into_iter().collect();
let drop_update = Update::CloseIndirect {
span: closed_id_and_metadata.clone(),
indirect_causes: indirect_causes.clone(),
};
for cause in &indirect_causes {
if &cause.id == &id {
is_cyclic = Some(indirect_causes.clone());
continue;
} else if let Some(cause) = ctx.span(&cause.id) {
let mut extensions = cause.extensions_mut();
if let Some(consequences) = extensions.get_mut::<Consequences>() {
consequences.remove_indirect(&closed_id_and_metadata);
}
if let Some(listeners) = extensions.get_mut::<Listeners>() {
channel::Sender::broadcast(listeners, drop_update.clone());
}
} else {
}
}
}
let direct_cause = span.parent();
if let Some(parent) = &direct_cause {
let mut parent_extensions = parent.extensions_mut();
if let Some(consequences) = parent_extensions.get_mut::<Consequences>() {
consequences.remove_direct(&closed_id_and_metadata);
}
}
if let Some(listeners) = extensions.get_mut::<Listeners>() {
let update = if let Some(indirect_causes) = is_cyclic {
Update::CloseCyclic {
span: closed_id_and_metadata,
direct_cause: direct_cause.map(|c| Span {
id: c.id(),
metadata: c.metadata(),
}),
indirect_causes,
}
} else {
Update::CloseDirect {
span: closed_id_and_metadata,
direct_cause: direct_cause.map(|c| Span {
id: c.id(),
metadata: c.metadata(),
}),
}
};
channel::Sender::broadcast(listeners, update);
}
}
}
#[cfg(test)]
mod test {
use crate::{self as causality, Consequences, Update};
use std::sync::Arc;
use tracing_core::Subscriber;
use tracing_subscriber::registry::{LookupSpan, SpanData};
use tracing_subscriber::{prelude::*, registry::Registry};
mod trace {
use crate::{self as causality};
use std::sync::Arc;
use tracing_core::Subscriber;
use tracing_subscriber::registry::{LookupSpan, SpanData};
use tracing_subscriber::{prelude::*, registry::Registry};
#[test]
fn should_install_listener() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
assert!(registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
let (_trace, _updates) =
causality::trace(registry, &a_id_and_metadata.id, 1024).unwrap();
assert_eq!(
registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
1
);
}
#[test]
fn should_copy_listener() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
assert!(registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
assert!(registry
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
let (_trace, _updates) =
causality::trace(registry, &a_id_and_metadata.id, 1024).unwrap();
assert_eq!(
registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
1
);
assert_eq!(
registry
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
1
);
}
#[test]
fn should_not_overwrite_listeners() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
assert!(registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
assert!(registry
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
let (_trace, _updates) =
causality::trace(registry, &b_id_and_metadata.id, 1024).unwrap();
assert!(registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
assert_eq!(
registry
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
1
);
let (_trace, _updates) =
causality::trace(registry, &a_id_and_metadata.id, 1024).unwrap();
assert_eq!(
registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
1
);
assert_eq!(
registry
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
2
);
}
}
mod layer {
mod on_new_span {
use crate::test::*;
#[test]
fn should_notify_listeners() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
assert!(registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_none());
let (_trace, updates) =
causality::trace(registry, &a_id_and_metadata.id, 1024).unwrap();
assert_eq!(
registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on this span")
.len(),
1
);
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
assert_eq!(
updates.next(),
Some(Update::OpenDirect {
cause: a_id_and_metadata,
consequence: b_id_and_metadata,
})
);
assert!(updates.is_empty());
}
#[test]
fn should_copy_listeners() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
let (_trace, _updates) =
causality::trace(registry, &a_id_and_metadata.id, 1024).unwrap();
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
let (_trace, _updates) =
causality::trace(registry, &b_id_and_metadata.id, 1024).unwrap();
let a_listeners = registry
.span_data(&a_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on span `a`")
.clone();
let b_listeners = registry
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.expect("there should be listeners on span `b`")
.clone();
dbg!(&a_listeners);
dbg!(&b_listeners);
assert!(
b_listeners.is_superset(&a_listeners),
"the listeners on `a` should have been copied to `b`"
);
assert_ne!(
b_listeners,
a_listeners,
"the listeners of `b` should not have been simply replaced the listeners on `a`"
);
}
#[test]
fn should_record_consequence() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
let a_consequences = causality::consequences(registry, &a_id_and_metadata.id)
.expect("span `a` should not have been closed yet");
assert_eq!(
a_consequences,
Consequences::default(),
"span `a` should not have any consequences"
);
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
let a_consequences = causality::consequences(registry, &a_id_and_metadata.id)
.expect("span `a` should not have been closed yet");
assert_eq!(
a_consequences,
Consequences::with_direct(b_id_and_metadata),
"span `a` should only have the direct consequence `b`"
);
}
}
mod on_follows_from {
use crate::test::*;
#[test]
fn should_record_consequence() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let cause = tracing::trace_span!("cause");
let cause_id_and_metadata = causality::Span {
id: cause.id().unwrap(),
metadata: cause.metadata().unwrap(),
};
let consequence = tracing::trace_span!("consequence");
let consequence_id_and_metadata = causality::Span {
id: consequence.id().unwrap(),
metadata: consequence.metadata().unwrap(),
};
let consequences = causality::consequences(registry, &cause_id_and_metadata.id)
.expect("span `cause` should not have been closed yet");
assert_eq!(
consequences,
Consequences::default(),
"span `cause` should not have any consequences"
);
consequence.follows_from(&cause_id_and_metadata.id);
let consequences = causality::consequences(registry, &cause_id_and_metadata.id)
.expect("span `cause` should not have been closed yet");
assert_eq!(
consequences,
Consequences::with_indirect(consequence_id_and_metadata),
"span `cause` should have an indirect `consequence`"
);
}
#[test]
fn should_record_cause() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let cause = tracing::trace_span!("cause");
let cause_id_and_metadata = causality::Span {
id: cause.id().unwrap(),
metadata: cause.metadata().unwrap(),
};
let consequence = tracing::trace_span!("consequence");
let consequence_id_and_metadata = causality::Span {
id: consequence.id().unwrap(),
metadata: consequence.metadata().unwrap(),
};
assert!(
registry
.span_data(&consequence_id_and_metadata.id)
.expect("span `consequence` should not yet be closed.")
.extensions()
.get::<crate::data::IndirectCauses>()
.is_none(),
"span `consequence` should not yet have `IndirectCauses`"
);
consequence.follows_from(&cause_id_and_metadata.id);
assert!(
registry
.span_data(&consequence_id_and_metadata.id)
.expect("span `consequence` should not yet be closed.")
.extensions()
.get::<crate::data::IndirectCauses>()
.expect("span `consequence` should have `IndirectCauses`")
.contains(&cause_id_and_metadata),
"`consequence`'s `IndirectCauses` should contain `cause_id_and_metadata`"
);
}
#[test]
fn should_notify_listeners() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let cause = tracing::trace_span!("cause");
let cause_id_and_metadata = causality::Span {
id: cause.id().unwrap(),
metadata: cause.metadata().unwrap(),
};
let consequence = tracing::trace_span!("consequence");
let consequence_id_and_metadata = causality::Span {
id: consequence.id().unwrap(),
metadata: consequence.metadata().unwrap(),
};
let (_trace, cause_updates) =
crate::trace(registry, &cause_id_and_metadata.id, 1024).unwrap();
let (_trace, consequence_updates) =
crate::trace(registry, &consequence_id_and_metadata.id, 1024).unwrap();
assert!(consequence_updates.is_empty());
assert!(cause_updates.is_empty());
consequence.follows_from(&cause_id_and_metadata.id);
assert!(
consequence_updates.is_empty(),
"The listeners on `consequence` should not have been \
notified of anything."
);
assert_eq!(
cause_updates.next(),
Some(Update::NewIndirect {
cause: cause_id_and_metadata.clone(),
consequence: consequence_id_and_metadata.clone(),
}),
"The listeners on `cause` should be notified that \
`consequence` indirectly follows from `cause`."
);
assert!(cause_updates.is_empty());
}
}
mod on_close {
use crate::test::*;
#[test]
fn should_erase_consequence() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let cause = tracing::trace_span!("cause");
let cause_id_and_metadata = causality::Span {
id: cause.id().unwrap(),
metadata: cause.metadata().unwrap(),
};
let consequence = tracing::trace_span!("consequence");
let consequence_id_and_metadata = causality::Span {
id: consequence.id().unwrap(),
metadata: consequence.metadata().unwrap(),
};
let (_trace, _updates) =
crate::trace(registry, &cause_id_and_metadata.id, 1024).unwrap();
let (_trace, _updates) =
crate::trace(registry, &consequence_id_and_metadata.id, 1024).unwrap();
assert_eq!(
causality::consequences(registry, &cause_id_and_metadata.id)
.expect("span `cause` should not have been closed yet"),
Consequences::default(),
"span `cause` should not have any consequences"
);
consequence.follows_from(&cause_id_and_metadata.id);
assert_eq!(
causality::consequences(registry, &cause_id_and_metadata.id)
.expect("span `cause` should not have been closed yet"),
Consequences::with_indirect(consequence_id_and_metadata.clone()),
"span `cause` should have one indirect consequence"
);
drop(consequence);
assert_eq!(
causality::consequences(registry, &cause_id_and_metadata.id)
.expect("span `cause` should not have been closed yet"),
Consequences::default(),
"span `cause` should not have any consequences"
);
}
#[test]
fn should_notify_causes_acyclic() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let cause = tracing::trace_span!("cause");
let cause_id_and_metadata = causality::Span {
id: cause.id().unwrap(),
metadata: cause.metadata().unwrap(),
};
let consequence = tracing::trace_span!("consequence");
let consequence_id_and_metadata = causality::Span {
id: consequence.id().unwrap(),
metadata: consequence.metadata().unwrap(),
};
consequence.follows_from(&cause_id_and_metadata.id);
let (_trace, cause_updates) =
crate::trace(registry, &cause_id_and_metadata.id, 1024).unwrap();
let (_trace, _consequence_updates) =
crate::trace(registry, &consequence_id_and_metadata.id, 1024).unwrap();
drop(consequence);
assert_eq!(
cause_updates.next(),
Some(Update::CloseIndirect {
span: consequence_id_and_metadata.clone(),
indirect_causes: vec![cause_id_and_metadata.clone()],
}),
"The listeners on `cause` should be notified that
`consequence` was closed."
);
assert!(cause_updates.is_empty());
}
#[test]
fn should_notify_causes_cyclic() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(causality::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let registry = subscriber.downcast_ref::<Registry>().unwrap();
let cause = tracing::trace_span!("cause");
let cause_id_and_metadata = causality::Span {
id: cause.id().unwrap(),
metadata: cause.metadata().unwrap(),
};
let consequence = cause.clone();
let consequence_id_and_metadata = causality::Span {
id: consequence.id().unwrap(),
metadata: consequence.metadata().unwrap(),
};
consequence.follows_from(&cause_id_and_metadata.id);
let (_trace, cause_updates) =
crate::trace(registry, &cause_id_and_metadata.id, 1024).unwrap();
let (_trace, _consequence_updates) =
crate::trace(registry, &consequence_id_and_metadata.id, 1024).unwrap();
drop([cause, consequence]);
assert_eq!(
cause_updates.next(),
Some(Update::CloseCyclic {
span: consequence_id_and_metadata.clone(),
direct_cause: None,
indirect_causes: vec![cause_id_and_metadata.clone()],
}),
"The listeners on `cause` should be notified that
`consequence` was closed."
);
assert!(cause_updates.is_empty());
}
}
}
}
#[cfg(test)]
mod test2 {
use crate::{self as causality};
use std::sync::Arc;
use tracing_core::Subscriber;
use tracing_subscriber::registry::{LookupSpan, SpanData};
use tracing_subscriber::{prelude::*, registry::Registry};
#[test]
fn should_update_transitively_1() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(crate::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let subscriber = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
let (trace, updates) = crate::trace(subscriber, &a_id_and_metadata.id, 1).unwrap();
assert!(trace
.consequences(&a_id_and_metadata)
.unwrap()
.contains_direct(&b_id_and_metadata));
let c = b.in_scope(|| tracing::trace_span!("c"));
let c_id_and_metadata = causality::Span {
id: c.id().unwrap(),
metadata: c.metadata().unwrap(),
};
dbg!(subscriber
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_some());
assert_eq!(
updates.next(),
Some(crate::Update::OpenDirect {
cause: b_id_and_metadata,
consequence: c_id_and_metadata,
})
);
}
#[test]
fn should_update_transitively_2() {
let subscriber: Arc<dyn Subscriber + Send + Sync> =
Arc::new(Registry::default().with(crate::Layer));
let _guard = subscriber.clone().set_default();
let subscriber: Arc<dyn Subscriber> = subscriber;
let subscriber = subscriber.downcast_ref::<Registry>().unwrap();
let a = tracing::trace_span!("a");
let a_id_and_metadata = causality::Span {
id: a.id().unwrap(),
metadata: a.metadata().unwrap(),
};
let (_trace, updates) = crate::trace(subscriber, &a_id_and_metadata.id, 1024).unwrap();
let b = a.in_scope(|| tracing::trace_span!("b"));
let b_id_and_metadata = causality::Span {
id: b.id().unwrap(),
metadata: b.metadata().unwrap(),
};
let c = b.in_scope(|| tracing::trace_span!("c"));
let c_id_and_metadata = causality::Span {
id: c.id().unwrap(),
metadata: c.metadata().unwrap(),
};
dbg!(subscriber
.span_data(&b_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_some());
dbg!(subscriber
.span_data(&c_id_and_metadata.id)
.unwrap()
.extensions()
.get::<crate::Listeners>()
.is_some());
assert_eq!(
updates.next(),
Some(crate::Update::OpenDirect {
cause: a_id_and_metadata,
consequence: b_id_and_metadata.clone(),
})
);
assert_eq!(
updates.next(),
Some(crate::Update::OpenDirect {
cause: b_id_and_metadata,
consequence: c_id_and_metadata,
})
);
}
}