Skip to content

Commit d31a36c

Browse files
committed
Fix tracing tests
1 parent 78cea8b commit d31a36c

File tree

11 files changed

+130
-532
lines changed

11 files changed

+130
-532
lines changed

sentry_sdk/integrations/opentelemetry/potel_span_processor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def _root_span_to_transaction_event(self, span):
179179

180180
transaction_name, transaction_source = extract_transaction_name_source(span)
181181
span_data = extract_span_data(span)
182-
(_, description, _, http_status, _) = span_data
182+
(_, description, status, http_status, _) = span_data
183183

184184
trace_context = get_trace_context(span, span_data=span_data)
185185
contexts = {"trace": trace_context}
@@ -241,6 +241,9 @@ def _span_to_json(self, span):
241241
}
242242
)
243243

244+
if status:
245+
span_json.setdefault("tags", {})["status"] = status
246+
244247
if parent_span_id:
245248
span_json["parent_span_id"] = parent_span_id
246249

sentry_sdk/integrations/opentelemetry/sampler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ def get_parent_sampled(parent_context, trace_id):
3434
# Only inherit sample rate if `traceId` is the same
3535
if is_span_context_valid and parent_context.trace_id == trace_id:
3636
# this is getSamplingDecision in JS
37-
if parent_context.trace_flags.sampled:
38-
return True
37+
if parent_context.trace_flags.sampled is not None:
38+
return parent_context.trace_flags.sampled
3939

4040
dsc_sampled = parent_context.trace_state.get(TRACESTATE_SAMPLED_KEY)
4141
if dsc_sampled == "true":

sentry_sdk/integrations/opentelemetry/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def trace_state_from_baggage(baggage):
336336

337337
def baggage_from_trace_state(trace_state):
338338
# type: (TraceState) -> Baggage
339-
return Baggage(dsc_from_trace_state(trace_state))
339+
return Baggage(dsc_from_trace_state(trace_state), mutable=False)
340340

341341

342342
def serialize_trace_state(trace_state):

sentry_sdk/scope.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -786,14 +786,6 @@ def span(self, span):
786786
# type: (Optional[Span]) -> None
787787
"""Set current tracing span."""
788788
self._span = span
789-
# XXX: this differs from the implementation in JS, there Scope.setSpan
790-
# does not set Scope._transactionName.
791-
if isinstance(span, Transaction):
792-
transaction = span
793-
if transaction.name:
794-
self._transaction = transaction.name
795-
if transaction.source:
796-
self._transaction_info["source"] = transaction.source
797789

798790
@property
799791
def profile(self):

sentry_sdk/tracing.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ class TransactionKwargs(SpanKwargs, total=False):
163163
}
164164

165165
DEFAULT_SPAN_ORIGIN = "manual"
166+
DEFAULT_SPAN_NAME = "<unlabeled span>"
166167

167168
tracer = otel_trace.get_tracer(__name__)
168169

@@ -1249,7 +1250,7 @@ def __init__(
12491250
# OTel timestamps have nanosecond precision
12501251
start_timestamp = convert_to_otel_timestamp(start_timestamp)
12511252

1252-
span_name = name or description or op or ""
1253+
span_name = name or description or op or DEFAULT_SPAN_NAME
12531254

12541255
# Prepopulate some attrs so that they're accessible in traces_sampler
12551256
attributes = attributes or {}
@@ -1398,7 +1399,9 @@ def span_id(self):
13981399
@property
13991400
def is_valid(self):
14001401
# type: () -> bool
1401-
return self._otel_span.get_span_context().is_valid
1402+
return self._otel_span.get_span_context().is_valid and isinstance(
1403+
self._otel_span, ReadableSpan
1404+
)
14021405

14031406
@property
14041407
def sampled(self):
Lines changed: 61 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
1-
import weakref
2-
import gc
31
import re
42
import pytest
53
import random
64

75
import sentry_sdk
86
from sentry_sdk import (
97
capture_message,
8+
continue_trace,
109
start_span,
11-
start_transaction,
1210
)
1311
from sentry_sdk.consts import SPANSTATUS
1412
from sentry_sdk.transport import Transport
15-
from sentry_sdk.tracing import Transaction
1613

1714

1815
@pytest.mark.parametrize("sample_rate", [0.0, 1.0])
1916
def test_basic(sentry_init, capture_events, sample_rate):
2017
sentry_init(traces_sample_rate=sample_rate)
2118
events = capture_events()
2219

23-
with start_transaction(name="hi") as transaction:
24-
transaction.set_status(SPANSTATUS.OK)
20+
with start_span(name="hi") as root_span:
21+
root_span.set_status(SPANSTATUS.OK)
2522
with pytest.raises(ZeroDivisionError):
2623
with start_span(op="foo", name="foodesc"):
2724
1 / 0
@@ -39,21 +36,23 @@ def test_basic(sentry_init, capture_events, sample_rate):
3936
span1, span2 = event["spans"]
4037
parent_span = event
4138
assert span1["tags"]["status"] == "internal_error"
39+
assert span1["status"] == "internal_error"
4240
assert span1["op"] == "foo"
4341
assert span1["description"] == "foodesc"
4442
assert "status" not in span2.get("tags", {})
4543
assert span2["op"] == "bar"
4644
assert span2["description"] == "bardesc"
4745
assert parent_span["transaction"] == "hi"
48-
assert "status" not in event["tags"]
46+
assert "status" not in event.get("tags", {})
4947
assert event["contexts"]["trace"]["status"] == "ok"
5048
else:
5149
assert not events
5250

5351

54-
@pytest.mark.parametrize("sampled", [True, False, None])
5552
@pytest.mark.parametrize("sample_rate", [0.0, 1.0])
56-
def test_continue_from_headers(sentry_init, capture_envelopes, sampled, sample_rate):
53+
def test_continue_trace(
54+
sentry_init, capture_envelopes, sample_rate, SortedBaggage
55+
): # noqa:N803
5756
"""
5857
Ensure data is actually passed along via headers, and that they are read
5958
correctly.
@@ -62,55 +61,41 @@ def test_continue_from_headers(sentry_init, capture_envelopes, sampled, sample_r
6261
envelopes = capture_envelopes()
6362

6463
# make a parent transaction (normally this would be in a different service)
65-
with start_transaction(name="hi", sampled=True if sample_rate == 0 else None):
66-
with start_span() as old_span:
67-
old_span.sampled = sampled
68-
headers = dict(
69-
sentry_sdk.get_current_scope().iter_trace_propagation_headers(old_span)
70-
)
71-
headers["baggage"] = (
72-
"other-vendor-value-1=foo;bar;baz, "
73-
"sentry-trace_id=771a43a4192642f0b136d5159a501700, "
74-
"sentry-public_key=49d0f7386ad645858ae85020e393bef3, "
75-
"sentry-sample_rate=0.01337, sentry-user_id=Amelie, "
76-
"other-vendor-value-2=foo;bar;"
77-
)
64+
with start_span(name="hi"):
65+
with start_span(name="inner") as old_span:
66+
headers = dict(old_span.iter_headers())
67+
assert headers["sentry-trace"]
68+
assert headers["baggage"]
7869

7970
# child transaction, to prove that we can read 'sentry-trace' header data correctly
80-
child_transaction = Transaction.continue_from_headers(headers, name="WRONG")
81-
assert child_transaction is not None
82-
assert child_transaction.parent_sampled == sampled
83-
assert child_transaction.trace_id == old_span.trace_id
84-
assert child_transaction.same_process_as_parent is False
85-
assert child_transaction.parent_span_id == old_span.span_id
86-
assert child_transaction.span_id != old_span.span_id
87-
88-
baggage = child_transaction._baggage
89-
assert baggage
90-
assert not baggage.mutable
91-
assert baggage.sentry_items == {
92-
"public_key": "49d0f7386ad645858ae85020e393bef3",
93-
"trace_id": "771a43a4192642f0b136d5159a501700",
94-
"user_id": "Amelie",
95-
"sample_rate": "0.01337",
96-
}
97-
98-
# add child transaction to the scope, to show that the captured message will
99-
# be tagged with the trace id (since it happens while the transaction is
100-
# open)
101-
with start_transaction(child_transaction):
102-
# change the transaction name from "WRONG" to make sure the change
103-
# is reflected in the final data
104-
sentry_sdk.get_current_scope().transaction = "ho"
105-
capture_message("hello")
71+
with continue_trace(headers):
72+
with start_span(name="WRONG") as child_root_span:
73+
assert child_root_span is not None
74+
assert child_root_span.sampled == (sample_rate == 1.0)
75+
if child_root_span.sampled:
76+
assert child_root_span.parent_span_id == old_span.span_id
77+
assert child_root_span.trace_id == old_span.trace_id
78+
assert child_root_span.span_id != old_span.span_id
79+
80+
baggage = child_root_span.get_baggage()
81+
assert baggage.serialize() == SortedBaggage(headers["baggage"])
82+
83+
# change the transaction name from "WRONG" to make sure the change
84+
# is reflected in the final data
85+
sentry_sdk.get_current_scope().set_transaction_name("ho")
86+
# to show that the captured message will be tagged with the trace id
87+
# (since it happens while the transaction is open)
88+
capture_message("hello")
10689

10790
# in this case the child transaction won't be captured
108-
if sampled is False or (sample_rate == 0 and sampled is None):
109-
trace1, message = envelopes
91+
# but message follows twp spec
92+
if sample_rate == 0.0:
93+
(message,) = envelopes
11094
message_payload = message.get_event()
111-
trace1_payload = trace1.get_transaction_event()
112-
113-
assert trace1_payload["transaction"] == "hi"
95+
assert message_payload["transaction"] == "ho"
96+
assert (
97+
child_root_span.trace_id == message_payload["contexts"]["trace"]["trace_id"]
98+
)
11499
else:
115100
trace1, message, trace2 = envelopes
116101
trace1_payload = trace1.get_transaction_event()
@@ -123,56 +108,43 @@ def test_continue_from_headers(sentry_init, capture_envelopes, sampled, sample_r
123108
assert (
124109
trace1_payload["contexts"]["trace"]["trace_id"]
125110
== trace2_payload["contexts"]["trace"]["trace_id"]
126-
== child_transaction.trace_id
111+
== child_root_span.trace_id
127112
== message_payload["contexts"]["trace"]["trace_id"]
128113
)
129114

130115
assert trace2.headers["trace"] == baggage.dynamic_sampling_context()
131-
assert trace2.headers["trace"] == {
132-
"public_key": "49d0f7386ad645858ae85020e393bef3",
133-
"trace_id": "771a43a4192642f0b136d5159a501700",
134-
"user_id": "Amelie",
135-
"sample_rate": "0.01337",
136-
}
137116

138117
assert message_payload["message"] == "hello"
139118

140119

141120
@pytest.mark.parametrize("sample_rate", [0.5, 1.0])
142121
def test_dynamic_sampling_head_sdk_creates_dsc(
143-
sentry_init, capture_envelopes, sample_rate, monkeypatch
122+
sentry_init,
123+
capture_envelopes,
124+
sample_rate,
125+
monkeypatch,
126+
SortedBaggage, # noqa: N803
144127
):
145128
sentry_init(traces_sample_rate=sample_rate, release="foo")
146129
envelopes = capture_envelopes()
147130

148131
# make sure transaction is sampled for both cases
149132
monkeypatch.setattr(random, "random", lambda: 0.1)
150133

151-
transaction = Transaction.continue_from_headers({}, name="Head SDK tx")
152-
153-
# will create empty mutable baggage
154-
baggage = transaction._baggage
155-
assert baggage
156-
assert baggage.mutable
157-
assert baggage.sentry_items == {}
158-
assert baggage.third_party_items == ""
159-
160-
with start_transaction(transaction):
161-
with start_span(op="foo", name="foodesc"):
162-
pass
134+
with continue_trace({}):
135+
with start_span(name="Head SDK tx"):
136+
with start_span(op="foo", name="foodesc") as span:
137+
baggage = span.get_baggage()
163138

164-
# finish will create a new baggage entry
165-
baggage = transaction._baggage
166-
trace_id = transaction.trace_id
139+
trace_id = span.trace_id
167140

168141
assert baggage
169-
assert not baggage.mutable
170142
assert baggage.third_party_items == ""
171143
assert baggage.sentry_items == {
172144
"environment": "production",
173145
"release": "foo",
174146
"sample_rate": str(sample_rate),
175-
"sampled": "true" if transaction.sampled else "false",
147+
"sampled": "true" if span.sampled else "false",
176148
"transaction": "Head SDK tx",
177149
"trace_id": trace_id,
178150
}
@@ -184,59 +156,30 @@ def test_dynamic_sampling_head_sdk_creates_dsc(
184156
"sentry-transaction=Head%%20SDK%%20tx,"
185157
"sentry-sample_rate=%s,"
186158
"sentry-sampled=%s"
187-
% (trace_id, sample_rate, "true" if transaction.sampled else "false")
159+
% (trace_id, sample_rate, "true" if span.sampled else "false")
188160
)
189-
assert baggage.serialize() == expected_baggage
161+
assert baggage.serialize() == SortedBaggage(expected_baggage)
190162

191163
(envelope,) = envelopes
192164
assert envelope.headers["trace"] == baggage.dynamic_sampling_context()
193165
assert envelope.headers["trace"] == {
194166
"environment": "production",
195167
"release": "foo",
196168
"sample_rate": str(sample_rate),
197-
"sampled": "true" if transaction.sampled else "false",
169+
"sampled": "true" if span.sampled else "false",
198170
"transaction": "Head SDK tx",
199171
"trace_id": trace_id,
200172
}
201173

202174

203-
@pytest.mark.parametrize(
204-
"args,expected_refcount",
205-
[({"traces_sample_rate": 1.0}, 100), ({"traces_sample_rate": 0.0}, 0)],
206-
)
207-
def test_memory_usage(sentry_init, capture_events, args, expected_refcount):
208-
sentry_init(**args)
209-
210-
references = weakref.WeakSet()
211-
212-
with start_transaction(name="hi"):
213-
for i in range(100):
214-
with start_span(op="helloworld", name="hi {}".format(i)) as span:
215-
216-
def foo():
217-
pass
218-
219-
references.add(foo)
220-
span.set_tag("foo", foo)
221-
pass
222-
223-
del foo
224-
del span
225-
226-
# required only for pypy (cpython frees immediately)
227-
gc.collect()
228-
229-
assert len(references) == expected_refcount
230-
231-
232175
def test_transactions_do_not_go_through_before_send(sentry_init, capture_events):
233176
def before_send(event, hint):
234177
raise RuntimeError("should not be called")
235178

236179
sentry_init(traces_sample_rate=1.0, before_send=before_send)
237180
events = capture_events()
238181

239-
with start_transaction(name="/"):
182+
with start_span(name="/"):
240183
pass
241184

242185
assert len(events) == 1
@@ -254,7 +197,7 @@ def capture_event(self, event):
254197
sentry_init(traces_sample_rate=1, transport=CustomTransport())
255198
events = capture_events()
256199

257-
with start_transaction(name="hi"):
200+
with start_span(name="hi"):
258201
with start_span(op="bar", name="bardesc"):
259202
pass
260203

@@ -264,14 +207,14 @@ def capture_event(self, event):
264207
def test_trace_propagation_meta_head_sdk(sentry_init):
265208
sentry_init(traces_sample_rate=1.0, release="foo")
266209

267-
transaction = Transaction.continue_from_headers({}, name="Head SDK tx")
268210
meta = None
269211
span = None
270212

271-
with start_transaction(transaction):
272-
with start_span(op="foo", name="foodesc") as current_span:
273-
span = current_span
274-
meta = sentry_sdk.get_current_scope().trace_propagation_meta()
213+
with continue_trace({}):
214+
with start_span(name="Head SDK tx") as root_span:
215+
with start_span(op="foo", name="foodesc") as current_span:
216+
span = current_span
217+
meta = sentry_sdk.get_current_scope().trace_propagation_meta()
275218

276219
ind = meta.find(">") + 1
277220
sentry_trace, baggage = meta[:ind], meta[ind:]
@@ -282,4 +225,4 @@ def test_trace_propagation_meta_head_sdk(sentry_init):
282225

283226
assert 'meta name="baggage"' in baggage
284227
baggage_content = re.findall('content="([^"]*)"', baggage)[0]
285-
assert baggage_content == transaction.get_baggage().serialize()
228+
assert baggage_content == root_span.get_baggage().serialize()

0 commit comments

Comments
 (0)