From 4be8f07adecb9bfe04b64853a9eb03a1d501a778 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 24 Feb 2025 12:52:14 +0100 Subject: [PATCH 1/2] Set HTTP client breadcrumb level based on status code --- sentry_sdk/integrations/aiohttp.py | 5 ++++- sentry_sdk/integrations/httpx.py | 2 ++ sentry_sdk/integrations/stdlib.py | 9 ++++++--- sentry_sdk/utils.py | 11 +++++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 9ebef2f9c6..86ea3218ee 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -27,6 +27,7 @@ capture_internal_exceptions, ensure_integration_enabled, event_from_exception, + http_client_status_to_breadcrumb_level, logger, parse_url, parse_version, @@ -277,13 +278,15 @@ async def on_request_end(session, trace_config_ctx, params): return span_data = trace_config_ctx.span_data or {} - span_data[SPANDATA.HTTP_STATUS_CODE] = int(params.response.status) + status_code = int(params.response.status) + span_data[SPANDATA.HTTP_STATUS_CODE] = status_code span_data["reason"] = params.response.reason sentry_sdk.add_breadcrumb( type="http", category="httplib", data=span_data, + level=http_client_status_to_breadcrumb_level(status_code), ) span = trace_config_ctx.span diff --git a/sentry_sdk/integrations/httpx.py b/sentry_sdk/integrations/httpx.py index 61188f9ef3..4c64f232ef 100644 --- a/sentry_sdk/integrations/httpx.py +++ b/sentry_sdk/integrations/httpx.py @@ -7,6 +7,7 @@ SENSITIVE_DATA_SUBSTITUTE, capture_internal_exceptions, ensure_integration_enabled, + http_client_status_to_breadcrumb_level, logger, parse_url, ) @@ -101,6 +102,7 @@ def send(self, request, **kwargs): type="http", category="httplib", data=data, + level=http_client_status_to_breadcrumb_level(rv.status_code), ) return rv diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py index 7b704593db..a6db07f48f 100644 --- a/sentry_sdk/integrations/stdlib.py +++ b/sentry_sdk/integrations/stdlib.py @@ -14,6 +14,7 @@ capture_internal_exceptions, ensure_integration_enabled, get_current_thread_meta, + http_client_status_to_breadcrumb_level, is_sentry_url, logger, safe_repr, @@ -144,14 +145,16 @@ def getresponse(self, *args, **kwargs): span_data[SPANDATA.HTTP_STATUS_CODE] = int(rv.status) span_data["reason"] = rv.reason + status_code = int(rv.status) + span.set_http_status(status_code) + span.set_data("reason", rv.reason) + sentry_sdk.add_breadcrumb( type="http", category="httplib", data=span_data, + level=http_client_status_to_breadcrumb_level(status_code), ) - - span.set_http_status(int(rv.status)) - span.set_data("reason", rv.reason) finally: span.__exit__(None, None, None) diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index ce1437222c..144e4f74a5 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -1887,3 +1887,14 @@ def should_be_treated_as_error(ty, value): return False return True + + +def http_client_status_to_breadcrumb_level(status_code): + # type: (Optional[int]) -> str + if status_code is not None: + if 500 <= status_code <= 599: + return "error" + elif 400 <= status_code <= 499: + return "warning" + + return "info" From 645506a4d794e955c8d79624ec3bd2c453b7883f Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 24 Feb 2025 13:38:41 +0100 Subject: [PATCH 2/2] fix tests --- tests/integrations/aiohttp/test_aiohttp.py | 9 +++------ tests/integrations/httpx/test_httpx.py | 11 +++-------- tests/integrations/requests/test_requests.py | 11 +++-------- tests/integrations/stdlib/test_httplib.py | 11 +++-------- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/tests/integrations/aiohttp/test_aiohttp.py b/tests/integrations/aiohttp/test_aiohttp.py index b3a1f1b6a7..f5fa766eae 100644 --- a/tests/integrations/aiohttp/test_aiohttp.py +++ b/tests/integrations/aiohttp/test_aiohttp.py @@ -534,8 +534,8 @@ async def handler(request): @pytest.mark.parametrize( "status_code,level", [ - (200, None), - (301, None), + (200, "info"), + (301, "info"), (403, "warning"), (405, "warning"), (500, "error"), @@ -570,10 +570,7 @@ async def handler(request): crumb = event["breadcrumbs"]["values"][0] assert crumb["type"] == "http" - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level + assert crumb["level"] == level assert crumb["category"] == "httplib" assert crumb["data"] == ApproxDict( { diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index f59c14e761..e5ebb19bde 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -64,8 +64,8 @@ def before_breadcrumb(crumb, hint): @pytest.mark.parametrize( "status_code,level", [ - (200, None), - (301, None), + (200, "info"), + (301, "info"), (403, "warning"), (405, "warning"), (500, "error"), @@ -98,12 +98,7 @@ def test_crumb_capture_client_error( crumb = event["breadcrumbs"]["values"][0] assert crumb["type"] == "http" assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - + assert crumb["level"] == level assert crumb["data"] == ApproxDict( { "url": url, diff --git a/tests/integrations/requests/test_requests.py b/tests/integrations/requests/test_requests.py index 8cfc0f932f..3862763a75 100644 --- a/tests/integrations/requests/test_requests.py +++ b/tests/integrations/requests/test_requests.py @@ -43,8 +43,8 @@ def test_crumb_capture(sentry_init, capture_events): @pytest.mark.parametrize( "status_code,level", [ - (200, None), - (301, None), + (200, "info"), + (301, "info"), (403, "warning"), (405, "warning"), (500, "error"), @@ -66,12 +66,7 @@ def test_crumb_capture_client_error(sentry_init, capture_events, status_code, le (crumb,) = event["breadcrumbs"]["values"] assert crumb["type"] == "http" assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - + assert crumb["level"] == level assert crumb["data"] == ApproxDict( { "url": url, diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index 0b09f8483a..1cad653558 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -70,8 +70,8 @@ def test_crumb_capture(sentry_init, capture_events): @pytest.mark.parametrize( "status_code,level", [ - (200, None), - (301, None), + (200, "info"), + (301, "info"), (403, "warning"), (405, "warning"), (500, "error"), @@ -94,12 +94,7 @@ def test_crumb_capture_client_error(sentry_init, capture_events, status_code, le assert crumb["type"] == "http" assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - + assert crumb["level"] == level assert crumb["data"] == ApproxDict( { "url": url,