Skip to content

Commit efbc35b

Browse files
committed
Add option to enable propagation of parent span tags
1 parent 46022c4 commit efbc35b

File tree

3 files changed

+80
-24
lines changed

3 files changed

+80
-24
lines changed

sentry-tracing/src/converters.rs

+39-18
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tracing_subscriber::layer::Context;
99
use tracing_subscriber::registry::LookupSpan;
1010

1111
use super::layer::SentrySpanData;
12-
use crate::TAGS_PREFIX;
12+
use crate::{SpanPropagation, TAGS_PREFIX};
1313

1414
/// Converts a [`tracing_core::Level`] to a Sentry [`Level`]
1515
fn convert_tracing_level(level: &tracing_core::Level) -> Level {
@@ -53,40 +53,34 @@ fn extract_event_data(event: &tracing_core::Event) -> (Option<String>, FieldVisi
5353

5454
fn extract_event_data_with_context<S>(
5555
event: &tracing_core::Event,
56-
ctx: Option<Context<S>>,
56+
ctx: Option<(SpanPropagation, Context<S>)>,
5757
) -> (Option<String>, FieldVisitor)
5858
where
5959
S: Subscriber + for<'a> LookupSpan<'a>,
6060
{
6161
let (message, mut visitor) = extract_event_data(event);
6262

6363
// Add the context fields of every parent span.
64-
let current_span = ctx.as_ref().and_then(|ctx| {
64+
let current_span = ctx.as_ref().and_then(|(propagation, ctx)| {
6565
event
6666
.parent()
67-
.and_then(|id| ctx.span(id))
68-
.or_else(|| ctx.lookup_current())
67+
.and_then(|id| ctx.span(id).map(|span| (*propagation, span)))
68+
.or_else(|| ctx.lookup_current().map(|span| (*propagation, span)))
6969
});
70-
if let Some(span) = current_span {
70+
if let Some((propagation, span)) = current_span {
7171
for span in span.scope() {
7272
let name = span.name();
7373
let ext = span.extensions();
7474
if let Some(span_data) = ext.get::<SentrySpanData>() {
7575
match &span_data.sentry_span {
7676
TransactionOrSpan::Span(span) => {
7777
for (key, value) in span.data().iter() {
78-
if key != "message" {
79-
let key = format!("{}:{}", name, key);
80-
visitor.json_values.insert(key, value.clone());
81-
}
78+
visitor.propagate_span_attr(key, value, propagation, name);
8279
}
8380
}
8481
TransactionOrSpan::Transaction(transaction) => {
8582
for (key, value) in transaction.data().iter() {
86-
if key != "message" {
87-
let key = format!("{}:{}", name, key);
88-
visitor.json_values.insert(key, value.clone());
89-
}
83+
visitor.propagate_span_attr(key, value, propagation, name);
9084
}
9185
}
9286
}
@@ -105,6 +99,26 @@ pub(crate) struct FieldVisitor {
10599
}
106100

107101
impl FieldVisitor {
102+
fn propagate_span_attr(
103+
&mut self,
104+
key: &str,
105+
value: &Value,
106+
span_propagation: SpanPropagation,
107+
span_name: &str,
108+
) {
109+
if key != "message" {
110+
if span_propagation.is_tags_enabled() && key.starts_with(TAGS_PREFIX) {
111+
//Propagate tags as it is, it will be extracted later on
112+
if !self.json_values.contains_key(key) {
113+
self.json_values.insert(key.to_owned(), value.clone());
114+
}
115+
} else if span_propagation.is_attrs_enabled() {
116+
let key = format!("{}:{}", span_name, key);
117+
self.json_values.insert(key, value.clone());
118+
}
119+
}
120+
}
121+
108122
fn record<T: Into<Value>>(&mut self, field: &Field, value: T) {
109123
self.json_values
110124
.insert(field.name().to_owned(), value.into());
@@ -143,12 +157,19 @@ impl Visit for FieldVisitor {
143157
/// Creates a [`Breadcrumb`] from a given [`tracing_core::Event`]
144158
pub fn breadcrumb_from_event<'context, S>(
145159
event: &tracing_core::Event,
146-
ctx: impl Into<Option<Context<'context, S>>>,
160+
ctx: impl Into<Option<(SpanPropagation, Context<'context, S>)>>,
147161
) -> Breadcrumb
148162
where
149163
S: Subscriber + for<'a> LookupSpan<'a>,
150164
{
151-
let (message, visitor) = extract_event_data_with_context(event, ctx.into());
165+
let ctx = match ctx.into() {
166+
Some((propagation, ctx)) if propagation.is_attrs_enabled() => {
167+
Some((SpanPropagation::Attributes, ctx))
168+
}
169+
//Breadcrumb has no tags, so propagate only attributes
170+
_ => None,
171+
};
172+
let (message, visitor) = extract_event_data_with_context(event, ctx);
152173
Breadcrumb {
153174
category: Some(event.metadata().target().to_owned()),
154175
ty: "log".into(),
@@ -219,7 +240,7 @@ fn contexts_from_event(
219240
/// Creates an [`Event`] from a given [`tracing_core::Event`]
220241
pub fn event_from_event<'context, S>(
221242
event: &tracing_core::Event,
222-
ctx: impl Into<Option<Context<'context, S>>>,
243+
ctx: impl Into<Option<(SpanPropagation, Context<'context, S>)>>,
223244
) -> Event<'static>
224245
where
225246
S: Subscriber + for<'a> LookupSpan<'a>,
@@ -239,7 +260,7 @@ where
239260
/// Creates an exception [`Event`] from a given [`tracing_core::Event`]
240261
pub fn exception_from_event<'context, S>(
241262
event: &tracing_core::Event,
242-
ctx: impl Into<Option<Context<'context, S>>>,
263+
ctx: impl Into<Option<(SpanPropagation, Context<'context, S>)>>,
243264
) -> Event<'static>
244265
where
245266
S: Subscriber + for<'a> LookupSpan<'a>,

sentry-tracing/src/layer.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use tracing_subscriber::layer::{Context, Layer};
1111
use tracing_subscriber::registry::LookupSpan;
1212

1313
use crate::converters::*;
14-
use crate::TAGS_PREFIX;
14+
use crate::{SpanPropagation, TAGS_PREFIX};
1515

1616
/// The action that Sentry should perform for a [`Metadata`]
1717
#[derive(Debug, Clone, Copy)]
@@ -70,7 +70,7 @@ pub struct SentryLayer<S> {
7070

7171
span_filter: Box<dyn Fn(&Metadata) -> bool + Send + Sync>,
7272

73-
with_span_attributes: bool,
73+
span_propagation: Option<SpanPropagation>,
7474
}
7575

7676
impl<S> SentryLayer<S> {
@@ -123,8 +123,14 @@ impl<S> SentryLayer<S> {
123123
/// the [traces_sample_rate][sentry_core::ClientOptions::traces_sample_rate] to `1.0`
124124
/// while configuring your sentry client.
125125
#[must_use]
126-
pub fn enable_span_attributes(mut self) -> Self {
127-
self.with_span_attributes = true;
126+
pub fn enable_span_attributes(self) -> Self {
127+
self.enable_span_propagation(SpanPropagation::Attributes)
128+
}
129+
130+
#[must_use]
131+
/// Configures span propagation for events' creation
132+
pub fn enable_span_propagation(mut self, propagation: SpanPropagation) -> Self {
133+
self.span_propagation = Some(propagation);
128134
self
129135
}
130136
}
@@ -140,7 +146,7 @@ where
140146

141147
span_filter: Box::new(default_span_filter),
142148

143-
with_span_attributes: false,
149+
span_propagation: None,
144150
}
145151
}
146152
}
@@ -210,7 +216,7 @@ where
210216
let item = match &self.event_mapper {
211217
Some(mapper) => mapper(event, ctx),
212218
None => {
213-
let span_ctx = self.with_span_attributes.then_some(ctx);
219+
let span_ctx = self.span_propagation.map(|propagation| (propagation, ctx));
214220
match (self.event_filter)(event.metadata()) {
215221
EventFilter::Ignore => EventMapping::Ignore,
216222
EventFilter::Breadcrumb => {

sentry-tracing/src/lib.rs

+29
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,32 @@ pub use converters::*;
147147
pub use layer::*;
148148

149149
const TAGS_PREFIX: &str = "tags.";
150+
151+
#[derive(Debug, Clone, Copy)]
152+
/// Controls propagation of span data when creating event
153+
///
154+
/// Note that the root span is considered a [transaction][sentry_core::protocol::Transaction]
155+
/// so its context will only be grabbed only if you set the transaction to be sampled.
156+
/// The most straightforward way to do this is to set
157+
/// the [traces_sample_rate][sentry_core::ClientOptions::traces_sample_rate] to `1.0`
158+
/// while configuring your sentry client.
159+
pub enum SpanPropagation {
160+
/// Collects all attributes prefixed with span name
161+
Attributes,
162+
/// Accumulates tags from within attributes as event tags, without overriding existing tags
163+
Tags,
164+
/// Collects both tags and attributes
165+
All,
166+
}
167+
168+
impl SpanPropagation {
169+
#[inline(always)]
170+
pub(crate) const fn is_tags_enabled(&self) -> bool {
171+
matches!(self, Self::Tags | Self::All)
172+
}
173+
174+
#[inline(always)]
175+
pub(crate) const fn is_attrs_enabled(&self) -> bool {
176+
matches!(self, Self::Attributes | Self::All)
177+
}
178+
}

0 commit comments

Comments
 (0)