use super::*;
use hdrhistogram::SyncHistogram;
use indexmap::IndexMap;
use std::hash::Hash;
use std::sync::atomic;
pub struct TimingLayer<S = group::ByName, E = group::ByMessage>
where
S: SpanGroup,
S::Id: Hash + Eq,
E: EventGroup,
E::Id: Hash + Eq,
{
timing: Timing<S, E>,
}
impl<S, E> std::fmt::Debug for TimingLayer<S, E>
where
S: SpanGroup + std::fmt::Debug,
S::Id: Hash + Eq + std::fmt::Debug,
E: EventGroup + std::fmt::Debug,
E::Id: Hash + Eq + std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TimingLayer")
.field("timing", &self.timing)
.finish()
}
}
impl<S, E> TimingLayer<S, E>
where
S: SpanGroup,
E: EventGroup,
S::Id: Hash + Eq,
E::Id: Hash + Eq,
{
pub(crate) fn new(timing: Timing<S, E>) -> Self {
Self { timing }
}
pub fn force_synchronize(&self) {
self.timing.force_synchronize()
}
pub fn with_histograms<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut HashMap<S::Id, IndexMap<E::Id, SyncHistogram<u64>, Hasher>>) -> R,
{
self.timing.with_histograms(f)
}
}
impl<S, SG, EG> tracing_subscriber::Layer<S> for TimingLayer<SG, EG>
where
S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
Self: 'static,
SG: SpanGroup,
EG: EventGroup,
SG::Id: Clone + Hash + Eq + Send + Sync,
EG::Id: Clone + Hash + Eq + Send + Sync,
{
fn on_new_span(
&self,
attrs: &span::Attributes,
id: &span::Id,
ctx: tracing_subscriber::layer::Context<S>,
) {
let group = self.timing.span_group.group(attrs);
self.timing.ensure_group(group.clone());
let span = ctx.span(id).unwrap();
span.extensions_mut().insert(SpanState {
group,
last_event: atomic::AtomicU64::new(self.timing.time.raw()),
});
}
fn on_event(&self, event: &Event, ctx: tracing_subscriber::layer::Context<S>) {
let span = event
.parent()
.cloned()
.or_else(|| ctx.current_span().id().cloned());
if let Some(id) = span {
let current = ctx.span(&id);
self.timing.time(event, |on_each| {
if let Some(ref span) = current {
{
let ext = span.extensions();
if !on_each(ext.get::<SpanState<SG::Id>>().unwrap()) {
return;
}
}
for span in span.scope().skip(1) {
let ext = span.extensions();
if !on_each(ext.get::<SpanState<SG::Id>>().unwrap()) {
break;
}
}
}
});
} else {
}
}
fn on_close(&self, id: span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
if self.timing.span_close_events {
let span = ctx.span(&id).expect("Span not found, this is a bug");
let meta = span.metadata();
let fs = field::FieldSet::new(&["message"], meta.callsite());
let fld = fs.iter().next().unwrap();
let v = [(&fld, Some(&"close" as &dyn field::Value))];
let vs = fs.value_set(&v);
let e = Event::new_child_of(id, meta, &vs);
self.timing.time(&e, |on_each| {
{
let ext = span.extensions();
if !on_each(ext.get::<SpanState<SG::Id>>().unwrap()) {
return;
}
}
for span in span.scope().skip(1) {
let ext = span.extensions();
if !on_each(ext.get::<SpanState<SG::Id>>().unwrap()) {
break;
}
}
});
}
}
}
#[derive(Debug, Copy)]
pub struct Downcaster<S, E> {
phantom: PhantomData<fn(S, E)>,
}
impl<S, E> Clone for Downcaster<S, E> {
fn clone(&self) -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<S, E> TimingLayer<S, E>
where
S: SpanGroup,
E: EventGroup,
S::Id: Clone + Hash + Eq,
E::Id: Clone + Hash + Eq,
{
pub fn downcaster(&self) -> Downcaster<S, E> {
Downcaster {
phantom: PhantomData,
}
}
}
impl<S, E> Downcaster<S, E>
where
S: SpanGroup + 'static,
E: EventGroup + 'static,
S::Id: Clone + Hash + Eq + 'static,
E::Id: Clone + Hash + Eq + 'static,
{
pub fn downcast<'a>(&self, d: &'a Dispatch) -> Option<&'a TimingLayer<S, E>> {
d.downcast_ref()
}
}