Skip to content

Commit 53de6aa

Browse files
authored
feat(tracing): Add tracestate feature flag (getsentry#1182)
This adds a feature flag, `"propagate_tracestate"`, to `_experiments`, which enables the tracestate header handling behavior. In particular, if the flag is `False` or missing: - `tracestate` headers won't be attached to outgoing HTTP requests, and - `trace` data will not be added to envelope headers.
1 parent 3877df9 commit 53de6aa

File tree

7 files changed

+84
-16
lines changed

7 files changed

+84
-16
lines changed

sentry_sdk/client.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from sentry_sdk.utils import ContextVar
2323
from sentry_sdk.sessions import SessionFlusher
2424
from sentry_sdk.envelope import Envelope
25-
from sentry_sdk.tracing_utils import reinflate_tracestate
25+
from sentry_sdk.tracing_utils import has_tracestate_enabled, reinflate_tracestate
2626

2727
from sentry_sdk._types import MYPY
2828

@@ -349,7 +349,7 @@ def capture_event(
349349
tracestate_data = raw_tracestate and reinflate_tracestate(
350350
raw_tracestate.replace("sentry=", "")
351351
)
352-
if tracestate_data:
352+
if tracestate_data and has_tracestate_enabled():
353353
headers["trace"] = tracestate_data
354354

355355
envelope = Envelope(headers=headers)

sentry_sdk/consts.py

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"max_spans": Optional[int],
3333
"record_sql_params": Optional[bool],
3434
"smart_transaction_trimming": Optional[bool],
35+
"propagate_tracestate": Optional[bool],
3536
},
3637
total=False,
3738
)

sentry_sdk/tracing.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
compute_tracestate_entry,
1313
extract_sentrytrace_data,
1414
extract_tracestate_data,
15+
has_tracestate_enabled,
1516
has_tracing_enabled,
1617
is_valid_sample_rate,
1718
maybe_create_breadcrumbs_from_span,
@@ -270,8 +271,10 @@ def iter_headers(self):
270271
"""
271272
yield "sentry-trace", self.to_traceparent()
272273

273-
tracestate = self.to_tracestate()
274+
tracestate = self.to_tracestate() if has_tracestate_enabled(self) else None
274275
# `tracestate` will only be `None` if there's no client or no DSN
276+
# TODO (kmclb) the above will be true once the feature is no longer
277+
# behind a flag
275278
if tracestate:
276279
yield "tracestate", tracestate
277280

sentry_sdk/tracing_utils.py

+9
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,12 @@ def _format_sql(cursor, sql):
396396
real_sql = None
397397

398398
return real_sql or to_string(sql)
399+
400+
401+
def has_tracestate_enabled(span=None):
402+
# type: (Optional[Span]) -> bool
403+
404+
client = ((span and span.hub) or sentry_sdk.Hub.current).client
405+
options = client and client.options
406+
407+
return bool(options and options["_experiments"].get("propagate_tracestate"))

tests/test_envelope.py

+38-11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
from sentry_sdk.tracing_utils import compute_tracestate_value
55
import sentry_sdk.client
66

7+
import pytest
8+
9+
try:
10+
from unittest import mock # python 3.3 and above
11+
except ImportError:
12+
import mock # python < 3.3
13+
714

815
def generate_transaction_item():
916
return {
@@ -25,6 +32,8 @@ def generate_transaction_item():
2532
"environment": "dogpark",
2633
"release": "off.leash.park",
2734
"public_key": "dogsarebadatkeepingsecrets",
35+
"user": {"id": 12312013, "segment": "bigs"},
36+
"transaction": "/interactions/other-dogs/new-dog",
2837
}
2938
),
3039
}
@@ -79,13 +88,23 @@ def test_add_and_get_session():
7988
assert item.payload.json == expected.to_json()
8089

8190

82-
def test_envelope_headers(sentry_init, capture_envelopes, monkeypatch):
91+
# TODO (kmclb) remove this parameterization once tracestate is a real feature
92+
@pytest.mark.parametrize("tracestate_enabled", [True, False])
93+
def test_envelope_headers(
94+
sentry_init, capture_envelopes, monkeypatch, tracestate_enabled
95+
):
8396
monkeypatch.setattr(
8497
sentry_sdk.client,
8598
"format_timestamp",
8699
lambda x: "2012-11-21T12:31:12.415908Z",
87100
)
88101

102+
monkeypatch.setattr(
103+
sentry_sdk.client,
104+
"has_tracestate_enabled",
105+
mock.Mock(return_value=tracestate_enabled),
106+
)
107+
89108
sentry_init(
90109
dsn="https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012",
91110
)
@@ -95,13 +114,21 @@ def test_envelope_headers(sentry_init, capture_envelopes, monkeypatch):
95114

96115
assert len(envelopes) == 1
97116

98-
assert envelopes[0].headers == {
99-
"event_id": "15210411201320122115110420122013",
100-
"sent_at": "2012-11-21T12:31:12.415908Z",
101-
"trace": {
102-
"trace_id": "12312012123120121231201212312012",
103-
"environment": "dogpark",
104-
"release": "off.leash.park",
105-
"public_key": "dogsarebadatkeepingsecrets",
106-
},
107-
}
117+
if tracestate_enabled:
118+
assert envelopes[0].headers == {
119+
"event_id": "15210411201320122115110420122013",
120+
"sent_at": "2012-11-21T12:31:12.415908Z",
121+
"trace": {
122+
"trace_id": "12312012123120121231201212312012",
123+
"environment": "dogpark",
124+
"release": "off.leash.park",
125+
"public_key": "dogsarebadatkeepingsecrets",
126+
"user": {"id": 12312013, "segment": "bigs"},
127+
"transaction": "/interactions/other-dogs/new-dog",
128+
},
129+
}
130+
else:
131+
assert envelopes[0].headers == {
132+
"event_id": "15210411201320122115110420122013",
133+
"sent_at": "2012-11-21T12:31:12.415908Z",
134+
}

tests/tracing/test_http_headers.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,9 @@ def test_tracestate_extraction(
282282
}
283283

284284

285-
def test_iter_headers(sentry_init, monkeypatch):
285+
# TODO (kmclb) remove this parameterization once tracestate is a real feature
286+
@pytest.mark.parametrize("tracestate_enabled", [True, False])
287+
def test_iter_headers(sentry_init, monkeypatch, tracestate_enabled):
286288
monkeypatch.setattr(
287289
Transaction,
288290
"to_traceparent",
@@ -293,6 +295,11 @@ def test_iter_headers(sentry_init, monkeypatch):
293295
"to_tracestate",
294296
mock.Mock(return_value="sentry=doGsaREgReaT,charlie=goofy"),
295297
)
298+
monkeypatch.setattr(
299+
sentry_sdk.tracing,
300+
"has_tracestate_enabled",
301+
mock.Mock(return_value=tracestate_enabled),
302+
)
296303

297304
transaction = Transaction(
298305
name="/interactions/other-dogs/new-dog",
@@ -303,7 +310,11 @@ def test_iter_headers(sentry_init, monkeypatch):
303310
assert (
304311
headers["sentry-trace"] == "12312012123120121231201212312012-0415201309082013-0"
305312
)
306-
assert headers["tracestate"] == "sentry=doGsaREgReaT,charlie=goofy"
313+
if tracestate_enabled:
314+
assert "tracestate" in headers
315+
assert headers["tracestate"] == "sentry=doGsaREgReaT,charlie=goofy"
316+
else:
317+
assert "tracestate" not in headers
307318

308319

309320
@pytest.mark.parametrize(

tests/tracing/test_misc.py

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from sentry_sdk import Hub, start_span, start_transaction
44
from sentry_sdk.tracing import Span, Transaction
5+
from sentry_sdk.tracing_utils import has_tracestate_enabled
56

67

78
def test_span_trimming(sentry_init, capture_events):
@@ -149,3 +150,19 @@ def test_finds_non_orphan_span_on_scope(sentry_init):
149150
assert scope._span is not None
150151
assert isinstance(scope._span, Span)
151152
assert scope._span.op == "sniffing"
153+
154+
155+
# TODO (kmclb) remove this test once tracestate is a real feature
156+
@pytest.mark.parametrize("tracestate_enabled", [True, False, None])
157+
def test_has_tracestate_enabled(sentry_init, tracestate_enabled):
158+
experiments = (
159+
{"propagate_tracestate": tracestate_enabled}
160+
if tracestate_enabled is not None
161+
else {}
162+
)
163+
sentry_init(_experiments=experiments)
164+
165+
if tracestate_enabled is True:
166+
assert has_tracestate_enabled() is True
167+
else:
168+
assert has_tracestate_enabled() is False

0 commit comments

Comments
 (0)