Skip to content

Commit f0ae4d8

Browse files
committed
Add option to enable propagation of parent span tags
1 parent 6a0ffeb commit f0ae4d8

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 {
@@ -54,40 +54,34 @@ fn extract_event_data(event: &tracing_core::Event) -> (Option<String>, FieldVisi
5454

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

6464
// Add the context fields of every parent span.
65-
let current_span = ctx.as_ref().and_then(|ctx| {
65+
let current_span = ctx.as_ref().and_then(|(propagation, ctx)| {
6666
event
6767
.parent()
68-
.and_then(|id| ctx.span(id))
69-
.or_else(|| ctx.lookup_current())
68+
.and_then(|id| ctx.span(id).map(|span| (*propagation, span)))
69+
.or_else(|| ctx.lookup_current().map(|span| (*propagation, span)))
7070
});
71-
if let Some(span) = current_span {
71+
if let Some((propagation, span)) = current_span {
7272
for span in span.scope() {
7373
let name = span.name();
7474
let ext = span.extensions();
7575
if let Some(span_data) = ext.get::<SentrySpanData>() {
7676
match &span_data.sentry_span {
7777
TransactionOrSpan::Span(span) => {
7878
for (key, value) in span.data().iter() {
79-
if key != "message" {
80-
let key = format!("{}:{}", name, key);
81-
visitor.json_values.insert(key, value.clone());
82-
}
79+
visitor.propagate_span_attr(key, value, propagation, name);
8380
}
8481
}
8582
TransactionOrSpan::Transaction(transaction) => {
8683
for (key, value) in transaction.data().iter() {
87-
if key != "message" {
88-
let key = format!("{}:{}", name, key);
89-
visitor.json_values.insert(key, value.clone());
90-
}
84+
visitor.propagate_span_attr(key, value, propagation, name);
9185
}
9286
}
9387
}
@@ -106,6 +100,26 @@ pub(crate) struct FieldVisitor {
106100
}
107101

108102
impl FieldVisitor {
103+
fn propagate_span_attr(
104+
&mut self,
105+
key: &str,
106+
value: &Value,
107+
span_propagation: SpanPropagation,
108+
span_name: &str,
109+
) {
110+
if key != "message" {
111+
if span_propagation.is_tags_enabled() && key.starts_with(TAGS_PREFIX) {
112+
//Propagate tags as it is, it will be extracted later on
113+
if !self.json_values.contains_key(key) {
114+
self.json_values.insert(key.to_owned(), value.clone());
115+
}
116+
} else if span_propagation.is_attrs_enabled() {
117+
let key = format!("{}:{}", span_name, key);
118+
self.json_values.insert(key, value.clone());
119+
}
120+
}
121+
}
122+
109123
fn record<T: Into<Value>>(&mut self, field: &Field, value: T) {
110124
self.json_values
111125
.insert(field.name().to_owned(), value.into());
@@ -144,12 +158,19 @@ impl Visit for FieldVisitor {
144158
/// Creates a [`Breadcrumb`] from a given [`tracing_core::Event`]
145159
pub fn breadcrumb_from_event<'context, S>(
146160
event: &tracing_core::Event,
147-
ctx: impl Into<Option<Context<'context, S>>>,
161+
ctx: impl Into<Option<(SpanPropagation, Context<'context, S>)>>,
148162
) -> Breadcrumb
149163
where
150164
S: Subscriber + for<'a> LookupSpan<'a>,
151165
{
152-
let (message, visitor) = extract_event_data_with_context(event, ctx.into());
166+
let ctx = match ctx.into() {
167+
Some((propagation, ctx)) if propagation.is_attrs_enabled() => {
168+
Some((SpanPropagation::Attributes, ctx))
169+
}
170+
//Breadcrumb has no tags, so propagate only attributes
171+
_ => None,
172+
};
173+
let (message, visitor) = extract_event_data_with_context(event, ctx);
153174
Breadcrumb {
154175
category: Some(event.metadata().target().to_owned()),
155176
ty: "log".into(),
@@ -220,7 +241,7 @@ fn contexts_from_event(
220241
/// Creates an [`Event`] from a given [`tracing_core::Event`]
221242
pub fn event_from_event<'context, S>(
222243
event: &tracing_core::Event,
223-
ctx: impl Into<Option<Context<'context, S>>>,
244+
ctx: impl Into<Option<(SpanPropagation, Context<'context, S>)>>,
224245
) -> Event<'static>
225246
where
226247
S: Subscriber + for<'a> LookupSpan<'a>,
@@ -240,7 +261,7 @@ where
240261
/// Creates an exception [`Event`] from a given [`tracing_core::Event`]
241262
pub fn exception_from_event<'context, S>(
242263
event: &tracing_core::Event,
243-
ctx: impl Into<Option<Context<'context, S>>>,
264+
ctx: impl Into<Option<(SpanPropagation, Context<'context, S>)>>,
244265
) -> Event<'static>
245266
where
246267
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)