Skip to content

Commit 077396c

Browse files
chore(sentry apps): Ignore a bunch more client side errors (#95829)
1 parent b2458bf commit 077396c

File tree

2 files changed

+140
-4
lines changed

2 files changed

+140
-4
lines changed

src/sentry/sentry_apps/tasks/sentry_apps.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from typing import Any
88

99
from django.urls import reverse
10-
from requests.exceptions import ChunkedEncodingError, RequestException
10+
from requests import HTTPError, Timeout
11+
from requests.exceptions import ChunkedEncodingError, ConnectionError, RequestException
1112

1213
from sentry import analytics, features, nodestore
1314
from sentry.api.serializers import serialize
@@ -78,8 +79,15 @@
7879
}
7980

8081
retry_decorator = retry(
81-
on=(RequestException, ApiHostError, ApiTimeoutError),
82-
on_silent=(ChunkedEncodingError),
82+
on=(RequestException),
83+
on_silent=(
84+
ChunkedEncodingError,
85+
Timeout,
86+
ApiHostError,
87+
ApiTimeoutError,
88+
ConnectionError,
89+
HTTPError,
90+
),
8391
ignore=(
8492
ClientError,
8593
SentryAppSentryError,

tests/sentry/sentry_apps/tasks/test_sentry_apps.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import pytest
55
import responses
66
from django.urls import reverse
7-
from requests.exceptions import ChunkedEncodingError
7+
from requests import HTTPError
8+
from requests.exceptions import ChunkedEncodingError, ConnectionError, Timeout
89

910
from sentry.api.serializers import serialize
1011
from sentry.api.serializers.rest_framework import convert_dict_key_case, snake_to_camel_case
@@ -71,6 +72,10 @@ def raiseException():
7172
raise Exception
7273

7374

75+
def raiseHTTPError():
76+
raise HTTPError()
77+
78+
7479
class RequestMock:
7580
def __init__(self):
7681
self.body = "blah blah"
@@ -99,6 +104,9 @@ def __init__(self):
99104
)
100105
MockResponseInstance = MockResponse({}, b"{}", "", True, 200, raiseStatusFalse, None)
101106
MockResponse404 = MockResponse({}, b'{"bruh": "bruhhhhhhh"}', "", False, 404, raiseException, None)
107+
MockResponse504 = MockResponse(headers, json_content, "", False, 504, raiseStatusFalse, None)
108+
MockResponse503 = MockResponse(headers, json_content, "", False, 503, raiseStatusFalse, None)
109+
MockResponse502 = MockResponse(headers, json_content, "", False, 502, raiseHTTPError, None)
102110

103111

104112
class TestSendAlertEvent(TestCase, OccurrenceTestMixin):
@@ -594,6 +602,126 @@ def test_ignores_restricted_ip_error(self, capture_exception, safe_urlopen):
594602
assert len(capture_exception.mock_calls) == 0
595603
assert safe_urlopen.called
596604

605+
@patch("sentry_sdk.capture_exception")
606+
def test_ignores_timeout_error(self, capture_exception, safe_urlopen):
607+
safe_urlopen.side_effect = Timeout()
608+
609+
event = self.store_event(data={}, project_id=self.project.id)
610+
assert event.group is not None
611+
612+
# The task should complete without reporting to sentry when Timeout is raised
613+
# because it's in the on_silent list of the retry decorator
614+
with self.tasks():
615+
post_process_group(
616+
is_new=True,
617+
is_regression=False,
618+
is_new_group_environment=False,
619+
cache_key=write_event_to_cache(event),
620+
group_id=event.group_id,
621+
project_id=self.project.id,
622+
eventstream_type=EventStreamEventType.Error.value,
623+
)
624+
625+
# Verify that the exception was not captured by Sentry since it's on_silent
626+
assert len(capture_exception.mock_calls) == 0
627+
assert safe_urlopen.called
628+
629+
@patch("sentry_sdk.capture_exception")
630+
def test_ignores_api_host_error(self, capture_exception, safe_urlopen):
631+
safe_urlopen.return_value = MockResponse503
632+
633+
event = self.store_event(data={}, project_id=self.project.id)
634+
assert event.group is not None
635+
636+
# The task should complete without reporting to sentry when ApiHostError is raised
637+
# because it's in the on_silent list of the retry decorator
638+
with self.tasks():
639+
post_process_group(
640+
is_new=True,
641+
is_regression=False,
642+
is_new_group_environment=False,
643+
cache_key=write_event_to_cache(event),
644+
group_id=event.group_id,
645+
project_id=self.project.id,
646+
eventstream_type=EventStreamEventType.Error.value,
647+
)
648+
649+
# Verify that the exception was not captured by Sentry since it's on_silent
650+
assert len(capture_exception.mock_calls) == 0
651+
assert safe_urlopen.called
652+
653+
@patch("sentry_sdk.capture_exception")
654+
def test_ignores_api_timeout_error(self, capture_exception, safe_urlopen):
655+
safe_urlopen.return_value = MockResponse504
656+
657+
event = self.store_event(data={}, project_id=self.project.id)
658+
assert event.group is not None
659+
660+
# The task should complete without reporting to sentry when ApiTimeoutError is raised
661+
# because it's in the on_silent list of the retry decorator
662+
with self.tasks():
663+
post_process_group(
664+
is_new=True,
665+
is_regression=False,
666+
is_new_group_environment=False,
667+
cache_key=write_event_to_cache(event),
668+
group_id=event.group_id,
669+
project_id=self.project.id,
670+
eventstream_type=EventStreamEventType.Error.value,
671+
)
672+
673+
# Verify that the exception was not captured by Sentry since it's on_silent
674+
assert len(capture_exception.mock_calls) == 0
675+
assert safe_urlopen.called
676+
677+
@patch("sentry_sdk.capture_exception")
678+
def test_ignores_connection_error(self, capture_exception, safe_urlopen):
679+
safe_urlopen.side_effect = ConnectionError()
680+
681+
event = self.store_event(data={}, project_id=self.project.id)
682+
assert event.group is not None
683+
684+
# The task should complete without reporting to sentry when ConnectionError is raised
685+
# because it's in the on_silent list of the retry decorator
686+
with self.tasks():
687+
post_process_group(
688+
is_new=True,
689+
is_regression=False,
690+
is_new_group_environment=False,
691+
cache_key=write_event_to_cache(event),
692+
group_id=event.group_id,
693+
project_id=self.project.id,
694+
eventstream_type=EventStreamEventType.Error.value,
695+
)
696+
697+
# Verify that the exception was not captured by Sentry since it's on_silent
698+
assert len(capture_exception.mock_calls) == 0
699+
assert safe_urlopen.called
700+
701+
@patch("sentry_sdk.capture_exception")
702+
def test_ignores_http_error(self, capture_exception, safe_urlopen):
703+
safe_urlopen.return_value = MockResponse502
704+
705+
event = self.store_event(data={}, project_id=self.project.id)
706+
assert event.group is not None
707+
708+
# The task should complete without reporting to sentry when HTTPError is raised
709+
# because it's in the on_silent list of the retry decorator
710+
with self.tasks():
711+
post_process_group(
712+
is_new=True,
713+
is_regression=False,
714+
is_new_group_environment=False,
715+
cache_key=write_event_to_cache(event),
716+
group_id=event.group_id,
717+
project_id=self.project.id,
718+
eventstream_type=EventStreamEventType.Error.value,
719+
)
720+
721+
# Verify that the exception was not captured by Sentry since it's on_silent
722+
assert len(capture_exception.mock_calls) == 0
723+
assert safe_urlopen.called
724+
597725
@patch("sentry_sdk.capture_exception")
598726
def test_silently_retries_chunked_encoding_error_unpublished(
599727
self, capture_exception, safe_urlopen

0 commit comments

Comments
 (0)