Skip to content

Commit 067d214

Browse files
authored
subscriber: add Context method for resolving an Event's SpanRef (#1434)
## Motivation Fixes #1428 ## Solution Adds a new `Context::event_span` method as proposed in the issue. No existing formatters were changed to use it yet, that seems like a secondary issue (especially if they already work correctly).
1 parent 7a01260 commit 067d214

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

tracing-subscriber/src/layer.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,78 @@ where
10031003
}
10041004
}
10051005

1006+
/// Returns a [`SpanRef`] for the parent span of the given [`Event`], if
1007+
/// it has a parent.
1008+
///
1009+
/// If the event has an explicitly overridden parent, this method returns
1010+
/// a reference to that span. If the event's parent is the current span,
1011+
/// this returns a reference to the current span, if there is one. If this
1012+
/// returns `None`, then either the event's parent was explicitly set to
1013+
/// `None`, or the event's parent was defined contextually, but no span
1014+
/// is currently entered.
1015+
///
1016+
/// Compared to [`Context::current_span`] and [`Context::lookup_current`],
1017+
/// this respects overrides provided by the [`Event`].
1018+
///
1019+
/// Compared to [`Event::parent`], this automatically falls back to the contextual
1020+
/// span, if required.
1021+
///
1022+
/// ```rust
1023+
/// use tracing::{Event, Subscriber};
1024+
/// use tracing_subscriber::{
1025+
/// layer::{Context, Layer},
1026+
/// prelude::*,
1027+
/// registry::LookupSpan,
1028+
/// };
1029+
///
1030+
/// struct PrintingLayer;
1031+
/// impl<S> Layer<S> for PrintingLayer
1032+
/// where
1033+
/// S: Subscriber + for<'lookup> LookupSpan<'lookup>,
1034+
/// {
1035+
/// fn on_event(&self, event: &Event, ctx: Context<S>) {
1036+
/// let span = ctx.event_span(event);
1037+
/// println!("Event in span: {:?}", span.map(|s| s.name()));
1038+
/// }
1039+
/// }
1040+
///
1041+
/// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || {
1042+
/// tracing::info!("no span");
1043+
/// // Prints: Event in span: None
1044+
///
1045+
/// let span = tracing::info_span!("span");
1046+
/// tracing::info!(parent: &span, "explicitly specified");
1047+
/// // Prints: Event in span: Some("span")
1048+
///
1049+
/// let _guard = span.enter();
1050+
/// tracing::info!("contextual span");
1051+
/// // Prints: Event in span: Some("span")
1052+
/// });
1053+
/// ```
1054+
///
1055+
/// <div class="example-wrap" style="display:inline-block">
1056+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
1057+
/// <strong>Note</strong>: This requires the wrapped subscriber to implement the
1058+
/// <a href="../registry/trait.LookupSpan.html"><code>LookupSpan</code></a> trait.
1059+
/// See the documentation on <a href="./struct.Context.html"><code>Context</code>'s
1060+
/// declaration</a> for details.
1061+
/// </pre></div>
1062+
#[inline]
1063+
#[cfg(feature = "registry")]
1064+
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
1065+
pub fn event_span(&self, event: &Event<'_>) -> Option<SpanRef<'_, S>>
1066+
where
1067+
S: for<'lookup> LookupSpan<'lookup>,
1068+
{
1069+
if event.is_root() {
1070+
None
1071+
} else if event.is_contextual() {
1072+
self.lookup_current()
1073+
} else {
1074+
event.parent().and_then(|id| self.span(id))
1075+
}
1076+
}
1077+
10061078
/// Returns metadata for the span with the given `id`, if it exists.
10071079
///
10081080
/// If this returns `None`, then no span exists for that ID (either it has
@@ -1165,6 +1237,36 @@ where
11651237
{
11661238
Some(self.span(id)?.scope())
11671239
}
1240+
1241+
/// Returns an iterator over the [stored data] for all the spans in the
1242+
/// current context, starting with the parent span of the specified event,
1243+
/// and ending with the root of the trace tree and ending with the current span.
1244+
///
1245+
/// <div class="example-wrap" style="display:inline-block">
1246+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
1247+
/// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
1248+
/// returns the spans in reverse order (from leaf to root). Use
1249+
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
1250+
/// in case root-to-leaf ordering is desired.
1251+
/// </pre></div>
1252+
///
1253+
/// <div class="example-wrap" style="display:inline-block">
1254+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
1255+
/// <strong>Note</strong>: This requires the wrapped subscriber to implement the
1256+
/// <a href="../registry/trait.LookupSpan.html"><code>LookupSpan</code></a> trait.
1257+
/// See the documentation on <a href="./struct.Context.html"><code>Context</code>'s
1258+
/// declaration</a> for details.
1259+
/// </pre></div>
1260+
///
1261+
/// [stored data]: ../registry/struct.SpanRef.html
1262+
#[cfg(feature = "registry")]
1263+
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
1264+
pub fn event_scope(&self, event: &Event<'_>) -> Option<registry::Scope<'_, S>>
1265+
where
1266+
S: for<'lookup> registry::LookupSpan<'lookup>,
1267+
{
1268+
Some(self.event_span(event)?.scope())
1269+
}
11681270
}
11691271

11701272
impl<'a, S> Context<'a, S> {
@@ -1194,6 +1296,8 @@ impl Identity {
11941296

11951297
#[cfg(test)]
11961298
pub(crate) mod tests {
1299+
use std::sync::{Arc, Mutex};
1300+
11971301
use super::*;
11981302

11991303
pub(crate) struct NopSubscriber;
@@ -1308,4 +1412,41 @@ pub(crate) mod tests {
13081412
<dyn Subscriber>::downcast_ref::<StringLayer3>(&s).expect("layer 3 should downcast");
13091413
assert_eq!(&layer.0, "layer_3");
13101414
}
1415+
1416+
#[test]
1417+
fn context_event_span() {
1418+
let last_event_span = Arc::new(Mutex::new(None));
1419+
1420+
struct RecordingLayer {
1421+
last_event_span: Arc<Mutex<Option<&'static str>>>,
1422+
}
1423+
1424+
impl<S> Layer<S> for RecordingLayer
1425+
where
1426+
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
1427+
{
1428+
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1429+
let span = ctx.event_span(event);
1430+
*self.last_event_span.lock().unwrap() = span.map(|s| s.name());
1431+
}
1432+
}
1433+
1434+
tracing::subscriber::with_default(
1435+
crate::registry().with(RecordingLayer {
1436+
last_event_span: last_event_span.clone(),
1437+
}),
1438+
|| {
1439+
tracing::info!("no span");
1440+
assert_eq!(*last_event_span.lock().unwrap(), None);
1441+
1442+
let parent = tracing::info_span!("explicit");
1443+
tracing::info!(parent: &parent, "explicit span");
1444+
assert_eq!(*last_event_span.lock().unwrap(), Some("explicit"));
1445+
1446+
let _guard = tracing::info_span!("contextual").entered();
1447+
tracing::info!("contextual span");
1448+
assert_eq!(*last_event_span.lock().unwrap(), Some("contextual"));
1449+
},
1450+
);
1451+
}
13111452
}

0 commit comments

Comments
 (0)