oauth2_broker/obs/
tracing.rs

1// self
2use crate::{_prelude::*, obs::FlowKind};
3
4/// Type alias that resolves to an instrumented future when tracing is enabled.
5#[cfg(feature = "tracing")]
6pub type InstrumentedFlow<F> = tracing::instrument::Instrumented<F>;
7/// Passthrough future type when tracing is disabled.
8#[cfg(not(feature = "tracing"))]
9pub type InstrumentedFlow<F> = F;
10
11/// A span builder used by broker flows.
12#[derive(Clone, Debug)]
13pub struct FlowSpan {
14	#[cfg(feature = "tracing")]
15	span: tracing::Span,
16}
17impl FlowSpan {
18	/// Creates a new span tagged with the provided flow kind + stage.
19	pub fn new(kind: FlowKind, stage: &'static str) -> Self {
20		#[cfg(feature = "tracing")]
21		{
22			let span = tracing::info_span!("oauth2_broker.flow", flow = kind.as_str(), stage);
23
24			Self { span }
25		}
26		#[cfg(not(feature = "tracing"))]
27		{
28			let _ = (kind, stage);
29
30			Self {}
31		}
32	}
33
34	/// Enters the span for synchronous sections.
35	pub fn entered(self) -> FlowSpanGuard {
36		#[cfg(feature = "tracing")]
37		{
38			FlowSpanGuard { guard: self.span.entered() }
39		}
40		#[cfg(not(feature = "tracing"))]
41		{
42			let _ = self;
43
44			FlowSpanGuard {}
45		}
46	}
47
48	/// Instruments an async block without holding a guard across `.await` points.
49	pub fn instrument<Fut>(&self, fut: Fut) -> InstrumentedFlow<Fut>
50	where
51		Fut: Future,
52	{
53		#[cfg(feature = "tracing")]
54		{
55			use tracing::Instrument;
56
57			fut.instrument(self.span.clone())
58		}
59		#[cfg(not(feature = "tracing"))]
60		{
61			fut
62		}
63	}
64}
65
66/// RAII guard returned by [`FlowSpan::entered`].
67pub struct FlowSpanGuard {
68	#[cfg(feature = "tracing")]
69	#[allow(dead_code)]
70	guard: tracing::span::EnteredSpan,
71}
72impl Debug for FlowSpanGuard {
73	fn fmt(&self, f: &mut Formatter) -> FmtResult {
74		f.write_str("FlowSpanGuard(..)")
75	}
76}
77
78#[cfg(test)]
79mod tests {
80	// self
81	use super::*;
82
83	#[test]
84	fn flow_span_noop_without_tracing() {
85		let _guard = FlowSpan::new(FlowKind::ClientCredentials, "test").entered();
86		// Compile-time smoke test ensures the guard exists even when tracing is disabled.
87	}
88
89	#[cfg(feature = "tracing")]
90	#[tokio::test]
91	async fn instrument_wraps_future() {
92		let span = FlowSpan::new(FlowKind::Refresh, "instrument_wraps_future");
93		let value = span.instrument(async { 42 }).await;
94
95		assert_eq!(value, 42);
96	}
97}