tide_server_timing/
timing_layer.rs1use tracing_core::span::{Attributes, Id};
2use tracing_core::{Dispatch, Subscriber};
3
4use tracing_subscriber::layer::Context;
5use tracing_subscriber::registry::LookupSpan;
6use tracing_subscriber::Layer;
7
8use std::iter::Extend;
9use std::{any::TypeId, marker::PhantomData, time::Instant};
10
11pub(crate) struct WithContext {
15 set: fn(&Dispatch, &Id, SpanRootTiming),
16 take: fn(&Dispatch, &Id, f: &mut dyn FnMut(SpanTiming)),
17}
18
19impl WithContext {
20 pub(crate) fn set<'a>(&self, dispatch: &'a Dispatch, id: &Id, value: SpanRootTiming) {
21 (self.set)(dispatch, id, value)
22 }
23
24 pub(crate) fn take<'a>(&self, dispatch: &'a Dispatch, id: &Id, f: &mut dyn FnMut(SpanTiming)) {
25 (self.take)(dispatch, id, f)
26 }
27}
28
29#[allow(missing_debug_implementations)]
31pub struct TimingLayer<S> {
32 ctx: WithContext,
33 _subscriber: PhantomData<fn(S)>,
34 _priv: (),
35}
36
37impl<S> TimingLayer<S>
38where
39 S: Subscriber + for<'span> LookupSpan<'span>,
40{
41 pub fn new() -> Self {
43 let ctx = WithContext {
44 set: Self::set_timing,
45 take: Self::take_timing,
46 };
47
48 Self {
49 ctx,
50 _subscriber: PhantomData,
51 _priv: (),
52 }
53 }
54
55 fn set_timing(dispatch: &Dispatch, id: &Id, timing: SpanRootTiming) {
56 let subscriber = dispatch
57 .downcast_ref::<S>()
58 .expect("subscriber should downcast to expected type; this is a bug!");
59 let span = subscriber
60 .span(id)
61 .expect("registry should have a span for the current ID");
62
63 let mut extensions = span.extensions_mut();
64 extensions.insert(timing);
65 }
66
67 fn take_timing(dispatch: &Dispatch, id: &Id, f: &mut dyn FnMut(SpanTiming)) {
68 let subscriber = dispatch
69 .downcast_ref::<S>()
70 .expect("subscriber should downcast to expected type; this is a bug!");
71 let span = subscriber
72 .span(id)
73 .expect("registry should have a span for the current ID");
74
75 let mut extensions = span.extensions_mut();
76 if let Some(value) = extensions.remove::<SpanTiming>() {
77 f(value);
78 }
79 }
80}
81
82impl<S> Layer<S> for TimingLayer<S>
83where
84 S: Subscriber + for<'a> LookupSpan<'a>,
85{
86 fn new_span(&self, _attrs: &Attributes<'_>, id: &Id, cx: Context<'_, S>) {
87 let span = cx.span(id).unwrap();
88 let meta = span.metadata();
89 let id = span.id();
90 let timing = SpanTiming::new(id, meta.name(), meta.target());
91 span.extensions_mut().insert(timing);
92 }
93
94 fn on_exit(&self, id: &Id, cx: Context<'_, S>) {
96 let span = cx.span(id).unwrap();
97
98 if span.extensions().get::<SpanRootTiming>().is_some() {
100 return;
101 };
102
103 let mut timing = match span.extensions_mut().remove::<SpanTiming>() {
105 Some(timing) => timing,
106 None => return,
107 };
108
109 timing.end_timing();
111
112 let span = cx.span(id).unwrap();
114 if let Some(parent_id) = span.parent_id() {
115 let parent = cx.span(parent_id).expect("parent not found");
116 if let Some(parent_timing) = parent.extensions_mut().get_mut::<SpanTiming>() {
117 parent_timing.children.extend(timing.flatten());
118 };
119 }
120 }
121
122 unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
123 match id {
124 id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
125 id if id == TypeId::of::<WithContext>() => Some(&self.ctx as *const _ as *const ()),
126 _ => None,
127 }
128 }
129}
130
131#[derive(Debug)]
133pub(crate) struct SpanRootTiming;
134
135#[derive(Debug)]
137pub(crate) struct SpanTiming {
138 pub(crate) target: &'static str,
139 pub(crate) id: Id,
140 pub(crate) span_name: &'static str,
141 pub(crate) start_time: Instant,
142 pub(crate) end_time: Option<Instant>,
143 children: Vec<Self>,
144}
145
146impl SpanTiming {
150 fn new(id: Id, span_name: &'static str, target: &'static str) -> Self {
151 Self {
152 start_time: Instant::now(),
153 end_time: None,
154 id,
155 span_name,
156 target,
157 children: vec![],
158 }
159 }
160
161 fn end_timing(&mut self) {
162 self.end_time = Some(Instant::now());
163 }
164
165 pub(crate) fn flatten(mut self) -> Vec<Self> {
166 let mut children = vec![];
167 std::mem::swap(&mut self.children, &mut children);
168 children.push(self);
169 children
170 }
171}