Skip to content

Commit ac8f812

Browse files
authored
Send all events to /envelope endpoint when tracing is enabled (#2009)
1 parent 5f2c34e commit ac8f812

File tree

4 files changed

+97
-48
lines changed

4 files changed

+97
-48
lines changed

sentry_sdk/client.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
logger,
1919
)
2020
from sentry_sdk.serializer import serialize
21-
from sentry_sdk.tracing import trace
21+
from sentry_sdk.tracing import trace, has_tracing_enabled
2222
from sentry_sdk.transport import make_transport
2323
from sentry_sdk.consts import (
2424
DEFAULT_OPTIONS,
@@ -495,6 +495,8 @@ def capture_event(
495495
if not is_transaction and not self._should_sample_error(event):
496496
return None
497497

498+
tracing_enabled = has_tracing_enabled(self.options)
499+
is_checkin = event_opt.get("type") == "check_in"
498500
attachments = hint.get("attachments")
499501

500502
dynamic_sampling_context = (
@@ -503,12 +505,12 @@ def capture_event(
503505
.pop("dynamic_sampling_context", {})
504506
)
505507

506-
is_checkin = event_opt.get("type") == "check_in"
507-
508-
# Transactions, events with attachments, and checkins should go to the /envelope/
509-
# endpoint.
510-
if is_transaction or is_checkin or attachments:
511-
508+
# If tracing is enabled all events should go to /envelope endpoint.
509+
# If no tracing is enabled only transactions, events with attachments, and checkins should go to the /envelope endpoint.
510+
should_use_envelope_endpoint = (
511+
tracing_enabled or is_transaction or is_checkin or bool(attachments)
512+
)
513+
if should_use_envelope_endpoint:
512514
headers = {
513515
"event_id": event_opt["event_id"],
514516
"sent_at": format_timestamp(datetime.utcnow()),
@@ -532,9 +534,11 @@ def capture_event(
532534
envelope.add_item(attachment.to_envelope_item())
533535

534536
self.transport.capture_envelope(envelope)
537+
535538
else:
536-
# All other events go to the /store/ endpoint.
539+
# All other events go to the legacy /store/ endpoint (will be removed in the future).
537540
self.transport.capture_event(event_opt)
541+
538542
return event_id
539543

540544
def capture_session(

tests/conftest.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,11 @@ def check_string_keys(map):
157157

158158
def check_envelope(envelope):
159159
with capture_internal_exceptions():
160-
# Assert error events are sent without envelope to server, for compat.
161-
# This does not apply if any item in the envelope is an attachment.
162-
if not any(x.type == "attachment" for x in envelope.items):
163-
assert not any(item.data_category == "error" for item in envelope.items)
164-
assert not any(item.get_event() is not None for item in envelope.items)
160+
# There used to be a check here for errors are not sent in envelopes.
161+
# We changed the behaviour to send errors in envelopes when tracing is enabled.
162+
# This is checked in test_client.py::test_sending_events_with_tracing
163+
# and test_client.py::test_sending_events_with_no_tracing
164+
pass
165165

166166
def inner(client):
167167
monkeypatch.setattr(

tests/integrations/gcp/test_gcp.py

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ def init_sdk(timeout_warning=False, **extra_init_args):
9494
def run_cloud_function():
9595
def inner(code, subprocess_kwargs=()):
9696

97-
event = []
98-
envelope = []
97+
events = []
98+
envelopes = []
9999
return_value = None
100100

101101
# STEP : Create a zip of cloud function
@@ -133,10 +133,10 @@ def inner(code, subprocess_kwargs=()):
133133
print("GCP:", line)
134134
if line.startswith("EVENT: "):
135135
line = line[len("EVENT: ") :]
136-
event = json.loads(line)
136+
events.append(json.loads(line))
137137
elif line.startswith("ENVELOPE: "):
138138
line = line[len("ENVELOPE: ") :]
139-
envelope = json.loads(line)
139+
envelopes.append(json.loads(line))
140140
elif line.startswith("RETURN VALUE: "):
141141
line = line[len("RETURN VALUE: ") :]
142142
return_value = json.loads(line)
@@ -145,13 +145,13 @@ def inner(code, subprocess_kwargs=()):
145145

146146
stream.close()
147147

148-
return envelope, event, return_value
148+
return envelopes, events, return_value
149149

150150
return inner
151151

152152

153153
def test_handled_exception(run_cloud_function):
154-
envelope, event, return_value = run_cloud_function(
154+
_, events, return_value = run_cloud_function(
155155
dedent(
156156
"""
157157
functionhandler = None
@@ -168,16 +168,16 @@ def cloud_function(functionhandler, event):
168168
"""
169169
)
170170
)
171-
assert event["level"] == "error"
172-
(exception,) = event["exception"]["values"]
171+
assert events[0]["level"] == "error"
172+
(exception,) = events[0]["exception"]["values"]
173173

174174
assert exception["type"] == "Exception"
175175
assert exception["value"] == "something went wrong"
176176
assert exception["mechanism"] == {"type": "gcp", "handled": False}
177177

178178

179179
def test_unhandled_exception(run_cloud_function):
180-
envelope, event, return_value = run_cloud_function(
180+
_, events, _ = run_cloud_function(
181181
dedent(
182182
"""
183183
functionhandler = None
@@ -195,16 +195,16 @@ def cloud_function(functionhandler, event):
195195
"""
196196
)
197197
)
198-
assert event["level"] == "error"
199-
(exception,) = event["exception"]["values"]
198+
assert events[0]["level"] == "error"
199+
(exception,) = events[0]["exception"]["values"]
200200

201201
assert exception["type"] == "ZeroDivisionError"
202202
assert exception["value"] == "division by zero"
203203
assert exception["mechanism"] == {"type": "gcp", "handled": False}
204204

205205

206206
def test_timeout_error(run_cloud_function):
207-
envelope, event, return_value = run_cloud_function(
207+
_, events, _ = run_cloud_function(
208208
dedent(
209209
"""
210210
functionhandler = None
@@ -222,8 +222,8 @@ def cloud_function(functionhandler, event):
222222
"""
223223
)
224224
)
225-
assert event["level"] == "error"
226-
(exception,) = event["exception"]["values"]
225+
assert events[0]["level"] == "error"
226+
(exception,) = events[0]["exception"]["values"]
227227

228228
assert exception["type"] == "ServerlessTimeoutWarning"
229229
assert (
@@ -234,7 +234,7 @@ def cloud_function(functionhandler, event):
234234

235235

236236
def test_performance_no_error(run_cloud_function):
237-
envelope, event, return_value = run_cloud_function(
237+
envelopes, _, _ = run_cloud_function(
238238
dedent(
239239
"""
240240
functionhandler = None
@@ -252,15 +252,15 @@ def cloud_function(functionhandler, event):
252252
)
253253
)
254254

255-
assert envelope["type"] == "transaction"
256-
assert envelope["contexts"]["trace"]["op"] == "function.gcp"
257-
assert envelope["transaction"].startswith("Google Cloud function")
258-
assert envelope["transaction_info"] == {"source": "component"}
259-
assert envelope["transaction"] in envelope["request"]["url"]
255+
assert envelopes[0]["type"] == "transaction"
256+
assert envelopes[0]["contexts"]["trace"]["op"] == "function.gcp"
257+
assert envelopes[0]["transaction"].startswith("Google Cloud function")
258+
assert envelopes[0]["transaction_info"] == {"source": "component"}
259+
assert envelopes[0]["transaction"] in envelopes[0]["request"]["url"]
260260

261261

262262
def test_performance_error(run_cloud_function):
263-
envelope, event, return_value = run_cloud_function(
263+
envelopes, events, _ = run_cloud_function(
264264
dedent(
265265
"""
266266
functionhandler = None
@@ -278,17 +278,18 @@ def cloud_function(functionhandler, event):
278278
)
279279
)
280280

281-
assert envelope["type"] == "transaction"
282-
assert envelope["contexts"]["trace"]["op"] == "function.gcp"
283-
assert envelope["transaction"].startswith("Google Cloud function")
284-
assert envelope["transaction"] in envelope["request"]["url"]
285-
assert event["level"] == "error"
286-
(exception,) = event["exception"]["values"]
281+
assert envelopes[0]["level"] == "error"
282+
(exception,) = envelopes[0]["exception"]["values"]
287283

288284
assert exception["type"] == "Exception"
289285
assert exception["value"] == "something went wrong"
290286
assert exception["mechanism"] == {"type": "gcp", "handled": False}
291287

288+
assert envelopes[1]["type"] == "transaction"
289+
assert envelopes[1]["contexts"]["trace"]["op"] == "function.gcp"
290+
assert envelopes[1]["transaction"].startswith("Google Cloud function")
291+
assert envelopes[1]["transaction"] in envelopes[0]["request"]["url"]
292+
292293

293294
def test_traces_sampler_gets_correct_values_in_sampling_context(
294295
run_cloud_function, DictionaryContaining # noqa:N803

tests/test_client.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ def test_init_string_types(dsn, sentry_init):
886886
)
887887

888888

889-
def test_envelope_types():
889+
def test_sending_events_with_tracing():
890890
"""
891891
Tests for calling the right transport method (capture_event vs
892892
capture_envelope) from the SDK client for different data types.
@@ -902,8 +902,56 @@ def capture_envelope(self, envelope):
902902
def capture_event(self, event):
903903
events.append(event)
904904

905-
with Hub(Client(traces_sample_rate=1.0, transport=CustomTransport())):
906-
event_id = capture_message("hello")
905+
with Hub(Client(enable_tracing=True, transport=CustomTransport())):
906+
try:
907+
1 / 0
908+
except Exception:
909+
event_id = capture_exception()
910+
911+
# Assert error events get passed in via capture_envelope
912+
assert not events
913+
envelope = envelopes.pop()
914+
(item,) = envelope.items
915+
assert item.data_category == "error"
916+
assert item.headers.get("type") == "event"
917+
assert item.get_event()["event_id"] == event_id
918+
919+
with start_transaction(name="foo"):
920+
pass
921+
922+
# Assert transactions get passed in via capture_envelope
923+
assert not events
924+
envelope = envelopes.pop()
925+
926+
(item,) = envelope.items
927+
assert item.data_category == "transaction"
928+
assert item.headers.get("type") == "transaction"
929+
930+
assert not envelopes
931+
assert not events
932+
933+
934+
def test_sending_events_with_no_tracing():
935+
"""
936+
Tests for calling the right transport method (capture_event vs
937+
capture_envelope) from the SDK client for different data types.
938+
"""
939+
940+
envelopes = []
941+
events = []
942+
943+
class CustomTransport(Transport):
944+
def capture_envelope(self, envelope):
945+
envelopes.append(envelope)
946+
947+
def capture_event(self, event):
948+
events.append(event)
949+
950+
with Hub(Client(enable_tracing=False, transport=CustomTransport())):
951+
try:
952+
1 / 0
953+
except Exception:
954+
event_id = capture_exception()
907955

908956
# Assert error events get passed in via capture_event
909957
assert not envelopes
@@ -917,11 +965,7 @@ def capture_event(self, event):
917965

918966
# Assert transactions get passed in via capture_envelope
919967
assert not events
920-
envelope = envelopes.pop()
921-
922-
(item,) = envelope.items
923-
assert item.data_category == "transaction"
924-
assert item.headers.get("type") == "transaction"
968+
assert not envelopes
925969

926970
assert not envelopes
927971
assert not events

0 commit comments

Comments
 (0)