Skip to content

Commit bbfb654

Browse files
feat: Option to not trace HTTP requests based on status codes
1 parent ceefa69 commit bbfb654

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

sentry_sdk/consts.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,7 @@ def __init__(
919919
max_stack_frames=DEFAULT_MAX_STACK_FRAMES, # type: Optional[int]
920920
enable_logs=False, # type: bool
921921
before_send_log=None, # type: Optional[Callable[[Log, Hint], Optional[Log]]]
922+
trace_ignore_status_codes=[], # type: Sequence[Union[int, Tuple[int, int]]]
922923
):
923924
# type: (...) -> None
924925
"""Initialize the Sentry SDK with the given parameters. All parameters described here can be used in a call to `sentry_sdk.init()`.
@@ -1307,6 +1308,16 @@ def __init__(
13071308
function will be retained. If the function returns None, the log will
13081309
not be sent to Sentry.
13091310
1311+
:param trace_ignore_status_codes: An optional property that disables tracing for
1312+
HTTP requests with certain response codes.
1313+
1314+
The option is a list, where elements are individual response codes, or inclusive
1315+
ranges of response codes. Requests are not traced if any code matches or
1316+
any provided range contains the response code.
1317+
1318+
If `trace_ignore_status_codes` is not provided, requests with any status code
1319+
may be traced.
1320+
13101321
:param _experiments:
13111322
"""
13121323
pass

sentry_sdk/tracing.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from typing import Tuple
3131
from typing import Union
3232
from typing import TypeVar
33+
from typing import Sequence
3334

3435
from typing_extensions import TypedDict, Unpack
3536

@@ -970,6 +971,30 @@ def _get_scope_from_finish_args(
970971

971972
return scope_or_hub
972973

974+
def _in_http_status_code_range(self, code, code_ranges):
975+
# type: (int, Sequence[Union[int, Tuple[int, int]]]) -> bool
976+
for target in code_ranges:
977+
if isinstance(target, int):
978+
print(code, target, code == target, type(code), type(target))
979+
if code == target:
980+
return True
981+
continue
982+
983+
if (
984+
len(target) != 2
985+
or not isinstance(target[0], int)
986+
or not isinstance(target[1], int)
987+
):
988+
logger.warning(
989+
"trace_ignore_status_codes must be a sequences including only integers or pairs of integers."
990+
)
991+
continue
992+
993+
if target[0] <= code <= target[1]:
994+
return True
995+
996+
return False
997+
973998
def finish(
974999
self,
9751000
scope=None, # type: Optional[sentry_sdk.Scope]
@@ -1039,6 +1064,12 @@ def finish(
10391064

10401065
super().finish(scope, end_timestamp)
10411066

1067+
if SPANDATA.HTTP_STATUS_CODE in self._data and self._in_http_status_code_range(
1068+
self._data[SPANDATA.HTTP_STATUS_CODE],
1069+
client.options["trace_ignore_status_codes"],
1070+
):
1071+
self.sampled = False
1072+
10421073
if not self.sampled:
10431074
# At this point a `sampled = None` should have already been resolved
10441075
# to a concrete decision.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import sentry_sdk
2+
from sentry_sdk import start_transaction, start_span
3+
4+
import pytest
5+
6+
7+
def test_no_ignored_codes(sentry_init, capture_events):
8+
sentry_init(
9+
traces_sample_rate=1.0,
10+
)
11+
events = capture_events()
12+
13+
with start_transaction(op="http", name="GET /"):
14+
span_or_tx = sentry_sdk.get_current_span()
15+
span_or_tx.set_data("http.response.status_code", 404)
16+
17+
assert len(events) == 1
18+
19+
20+
@pytest.mark.parametrize("status_code", [200, 404])
21+
def test_single_code_ignored(sentry_init, capture_events, status_code):
22+
sentry_init(
23+
traces_sample_rate=1.0,
24+
trace_ignore_status_codes=(404,),
25+
)
26+
events = capture_events()
27+
28+
with start_transaction(op="http", name="GET /"):
29+
span_or_tx = sentry_sdk.get_current_span()
30+
span_or_tx.set_data("http.response.status_code", status_code)
31+
32+
if status_code == 404:
33+
assert not events
34+
else:
35+
assert len(events) == 1
36+
37+
38+
@pytest.mark.parametrize("status_code", [200, 305, 307, 399, 404])
39+
def test_range_ignored(sentry_init, capture_events, status_code):
40+
sentry_init(
41+
traces_sample_rate=1.0,
42+
trace_ignore_status_codes=((305, 399),),
43+
)
44+
events = capture_events()
45+
46+
with start_transaction(op="http", name="GET /"):
47+
span_or_tx = sentry_sdk.get_current_span()
48+
span_or_tx.set_data("http.response.status_code", status_code)
49+
50+
if 305 <= status_code <= 399:
51+
assert not events
52+
else:
53+
assert len(events) == 1
54+
55+
56+
@pytest.mark.parametrize("status_code", [200, 301, 303, 355, 404])
57+
def test_variety_ignored(sentry_init, capture_events, status_code):
58+
sentry_init(
59+
traces_sample_rate=1.0,
60+
trace_ignore_status_codes=(
61+
301,
62+
302,
63+
303,
64+
(305, 399),
65+
(401, 404),
66+
),
67+
)
68+
events = capture_events()
69+
70+
with start_transaction(op="http", name="GET /"):
71+
span_or_tx = sentry_sdk.get_current_span()
72+
span_or_tx.set_data("http.response.status_code", status_code)
73+
74+
if (
75+
301 <= status_code <= 303
76+
or 305 <= status_code <= 399
77+
or 401 <= status_code <= 404
78+
):
79+
assert not events
80+
else:
81+
assert len(events) == 1

0 commit comments

Comments
 (0)