Skip to content

Commit b7fd54a

Browse files
authored
Improved handling of span status (#3261)
1 parent 79e8970 commit b7fd54a

File tree

10 files changed

+90
-52
lines changed

10 files changed

+90
-52
lines changed

sentry_sdk/consts.py

+26
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,32 @@ class SPANDATA:
386386
"""
387387

388388

389+
class SPANSTATUS:
390+
"""
391+
The status of a Sentry span.
392+
393+
See: https://develop.sentry.dev/sdk/event-payloads/contexts/#trace-context
394+
"""
395+
396+
ABORTED = "aborted"
397+
ALREADY_EXISTS = "already_exists"
398+
CANCELLED = "cancelled"
399+
DATA_LOSS = "data_loss"
400+
DEADLINE_EXCEEDED = "deadline_exceeded"
401+
FAILED_PRECONDITION = "failed_precondition"
402+
INTERNAL_ERROR = "internal_error"
403+
INVALID_ARGUMENT = "invalid_argument"
404+
NOT_FOUND = "not_found"
405+
OK = "ok"
406+
OUT_OF_RANGE = "out_of_range"
407+
PERMISSION_DENIED = "permission_denied"
408+
RESOURCE_EXHAUSTED = "resource_exhausted"
409+
UNAUTHENTICATED = "unauthenticated"
410+
UNAVAILABLE = "unavailable"
411+
UNIMPLEMENTED = "unimplemented"
412+
UNKNOWN_ERROR = "unknown_error"
413+
414+
389415
class OP:
390416
ANTHROPIC_MESSAGES_CREATE = "ai.messages.create.anthropic"
391417
CACHE_GET = "cache.get"

sentry_sdk/integrations/aiohttp.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import sentry_sdk
55
from sentry_sdk.api import continue_trace
6-
from sentry_sdk.consts import OP, SPANDATA
6+
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
77
from sentry_sdk.integrations import Integration, DidNotEnable
88
from sentry_sdk.integrations.logging import ignore_logger
99
from sentry_sdk.scope import Scope
@@ -133,7 +133,7 @@ async def sentry_app_handle(self, request, *args, **kwargs):
133133
transaction.set_http_status(e.status_code)
134134
raise
135135
except (asyncio.CancelledError, ConnectionResetError):
136-
transaction.set_status("cancelled")
136+
transaction.set_status(SPANSTATUS.CANCELLED)
137137
raise
138138
except Exception:
139139
# This will probably map to a 500 but seems like we

sentry_sdk/integrations/arq.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sentry_sdk
44
from sentry_sdk._types import TYPE_CHECKING
5-
from sentry_sdk.consts import OP
5+
from sentry_sdk.consts import OP, SPANSTATUS
66
from sentry_sdk.integrations import DidNotEnable, Integration
77
from sentry_sdk.integrations.logging import ignore_logger
88
from sentry_sdk.scope import Scope, should_send_default_pii
@@ -119,10 +119,10 @@ def _capture_exception(exc_info):
119119

120120
if scope.transaction is not None:
121121
if exc_info[0] in ARQ_CONTROL_FLOW_EXCEPTIONS:
122-
scope.transaction.set_status("aborted")
122+
scope.transaction.set_status(SPANSTATUS.ABORTED)
123123
return
124124

125-
scope.transaction.set_status("internal_error")
125+
scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR)
126126

127127
event, hint = event_from_exception(
128128
exc_info,

sentry_sdk/integrations/celery/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sentry_sdk
66
from sentry_sdk import isolation_scope
77
from sentry_sdk.api import continue_trace
8-
from sentry_sdk.consts import OP, SPANDATA
8+
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
99
from sentry_sdk.integrations import Integration, DidNotEnable
1010
from sentry_sdk.integrations.celery.beat import (
1111
_patch_beat_apply_entry,
@@ -317,7 +317,7 @@ def _inner(*args, **kwargs):
317317
origin=CeleryIntegration.origin,
318318
)
319319
transaction.name = task.name
320-
transaction.set_status("ok")
320+
transaction.set_status(SPANSTATUS.OK)
321321

322322
if transaction is None:
323323
return f(*args, **kwargs)

sentry_sdk/integrations/huey.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sentry_sdk
55
from sentry_sdk._types import TYPE_CHECKING
66
from sentry_sdk.api import continue_trace, get_baggage, get_traceparent
7-
from sentry_sdk.consts import OP
7+
from sentry_sdk.consts import OP, SPANSTATUS
88
from sentry_sdk.integrations import DidNotEnable, Integration
99
from sentry_sdk.scope import Scope, should_send_default_pii
1010
from sentry_sdk.tracing import (
@@ -109,10 +109,10 @@ def _capture_exception(exc_info):
109109
scope = Scope.get_current_scope()
110110

111111
if exc_info[0] in HUEY_CONTROL_FLOW_EXCEPTIONS:
112-
scope.transaction.set_status("aborted")
112+
scope.transaction.set_status(SPANSTATUS.ABORTED)
113113
return
114114

115-
scope.transaction.set_status("internal_error")
115+
scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR)
116116
event, hint = event_from_exception(
117117
exc_info,
118118
client_options=Scope.get_client().options,
@@ -161,7 +161,7 @@ def _sentry_execute(self, task, timestamp=None):
161161
source=TRANSACTION_SOURCE_TASK,
162162
origin=HueyIntegration.origin,
163163
)
164-
transaction.set_status("ok")
164+
transaction.set_status(SPANSTATUS.OK)
165165

166166
if not getattr(task, "_sentry_is_patched", False):
167167
task.execute = _wrap_task_execute(task.execute)

sentry_sdk/integrations/opentelemetry/span_processor.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
INVALID_TRACE_ID,
1717
)
1818
from sentry_sdk import get_client, start_transaction
19-
from sentry_sdk.consts import INSTRUMENTER
19+
from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS
2020
from sentry_sdk.integrations.opentelemetry.consts import (
2121
SENTRY_BAGGAGE_KEY,
2222
SENTRY_TRACE_KEY,
@@ -299,10 +299,10 @@ def _update_span_with_otel_status(self, sentry_span, otel_span):
299299
return
300300

301301
if otel_span.status.is_ok:
302-
sentry_span.set_status("ok")
302+
sentry_span.set_status(SPANSTATUS.OK)
303303
return
304304

305-
sentry_span.set_status("internal_error")
305+
sentry_span.set_status(SPANSTATUS.INTERNAL_ERROR)
306306

307307
def _update_span_with_otel_data(self, sentry_span, otel_span):
308308
# type: (SentrySpan, OTelSpan) -> None

sentry_sdk/integrations/pymongo.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import copy
22

33
import sentry_sdk
4-
from sentry_sdk.consts import SPANDATA, OP
4+
from sentry_sdk.consts import SPANSTATUS, SPANDATA, OP
55
from sentry_sdk.integrations import DidNotEnable, Integration
66
from sentry_sdk.scope import should_send_default_pii
77
from sentry_sdk.tracing import Span
@@ -181,7 +181,7 @@ def failed(self, event):
181181

182182
try:
183183
span = self._ongoing_operations.pop(self._operation_key(event))
184-
span.set_status("internal_error")
184+
span.set_status(SPANSTATUS.INTERNAL_ERROR)
185185
span.__exit__(None, None, None)
186186
except KeyError:
187187
return
@@ -193,7 +193,7 @@ def succeeded(self, event):
193193

194194
try:
195195
span = self._ongoing_operations.pop(self._operation_key(event))
196-
span.set_status("ok")
196+
span.set_status(SPANSTATUS.OK)
197197
span.__exit__(None, None, None)
198198
except KeyError:
199199
pass

sentry_sdk/integrations/sqlalchemy.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sentry_sdk
22
from sentry_sdk._types import TYPE_CHECKING
3-
from sentry_sdk.consts import SPANDATA
3+
from sentry_sdk.consts import SPANSTATUS, SPANDATA
44
from sentry_sdk.db.explain_plan.sqlalchemy import attach_explain_plan_to_span
55
from sentry_sdk.integrations import Integration, DidNotEnable
66
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
@@ -107,7 +107,7 @@ def _handle_error(context, *args):
107107
span = getattr(execution_context, "_sentry_sql_span", None) # type: Optional[Span]
108108

109109
if span is not None:
110-
span.set_status("internal_error")
110+
span.set_status(SPANSTATUS.INTERNAL_ERROR)
111111

112112
# _after_cursor_execute does not get called for crashing SQL stmts. Judging
113113
# from SQLAlchemy codebase it does seem like any error coming into this

sentry_sdk/tracing.py

+43-32
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import datetime, timedelta, timezone
44

55
import sentry_sdk
6-
from sentry_sdk.consts import INSTRUMENTER, SPANDATA
6+
from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA
77
from sentry_sdk.profiler.continuous_profiler import get_profiler_id
88
from sentry_sdk.utils import (
99
get_current_thread_meta,
@@ -149,6 +149,45 @@ class TransactionKwargs(SpanKwargs, total=False):
149149
}
150150

151151

152+
def get_span_status_from_http_code(http_status_code):
153+
# type: (int) -> str
154+
"""
155+
Returns the Sentry status corresponding to the given HTTP status code.
156+
157+
See: https://develop.sentry.dev/sdk/event-payloads/contexts/#trace-context
158+
"""
159+
if http_status_code < 400:
160+
return SPANSTATUS.OK
161+
162+
elif 400 <= http_status_code < 500:
163+
if http_status_code == 403:
164+
return SPANSTATUS.PERMISSION_DENIED
165+
elif http_status_code == 404:
166+
return SPANSTATUS.NOT_FOUND
167+
elif http_status_code == 429:
168+
return SPANSTATUS.RESOURCE_EXHAUSTED
169+
elif http_status_code == 413:
170+
return SPANSTATUS.FAILED_PRECONDITION
171+
elif http_status_code == 401:
172+
return SPANSTATUS.UNAUTHENTICATED
173+
elif http_status_code == 409:
174+
return SPANSTATUS.ALREADY_EXISTS
175+
else:
176+
return SPANSTATUS.INVALID_ARGUMENT
177+
178+
elif 500 <= http_status_code < 600:
179+
if http_status_code == 504:
180+
return SPANSTATUS.DEADLINE_EXCEEDED
181+
elif http_status_code == 501:
182+
return SPANSTATUS.UNIMPLEMENTED
183+
elif http_status_code == 503:
184+
return SPANSTATUS.UNAVAILABLE
185+
else:
186+
return SPANSTATUS.INTERNAL_ERROR
187+
188+
return SPANSTATUS.UNKNOWN_ERROR
189+
190+
152191
class _SpanRecorder:
153192
"""Limits the number of spans recorded in a transaction."""
154193

@@ -317,7 +356,7 @@ def __enter__(self):
317356
def __exit__(self, ty, value, tb):
318357
# type: (Optional[Any], Optional[Any], Optional[Any]) -> None
319358
if value is not None:
320-
self.set_status("internal_error")
359+
self.set_status(SPANSTATUS.INTERNAL_ERROR)
321360

322361
scope, old_span = self._context_manager_state
323362
del self._context_manager_state
@@ -540,37 +579,9 @@ def set_http_status(self, http_status):
540579
# type: (int) -> None
541580
self.set_tag(
542581
"http.status_code", str(http_status)
543-
) # we keep this for backwards compatability
582+
) # we keep this for backwards compatibility
544583
self.set_data(SPANDATA.HTTP_STATUS_CODE, http_status)
545-
546-
if http_status < 400:
547-
self.set_status("ok")
548-
elif 400 <= http_status < 500:
549-
if http_status == 403:
550-
self.set_status("permission_denied")
551-
elif http_status == 404:
552-
self.set_status("not_found")
553-
elif http_status == 429:
554-
self.set_status("resource_exhausted")
555-
elif http_status == 413:
556-
self.set_status("failed_precondition")
557-
elif http_status == 401:
558-
self.set_status("unauthenticated")
559-
elif http_status == 409:
560-
self.set_status("already_exists")
561-
else:
562-
self.set_status("invalid_argument")
563-
elif 500 <= http_status < 600:
564-
if http_status == 504:
565-
self.set_status("deadline_exceeded")
566-
elif http_status == 501:
567-
self.set_status("unimplemented")
568-
elif http_status == 503:
569-
self.set_status("unavailable")
570-
else:
571-
self.set_status("internal_error")
572-
else:
573-
self.set_status("unknown_error")
584+
self.set_status(get_span_status_from_http_code(http_status))
574585

575586
def is_success(self):
576587
# type: () -> bool

tests/tracing/test_integration_tests.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
start_span,
1111
start_transaction,
1212
)
13+
from sentry_sdk.consts import SPANSTATUS
1314
from sentry_sdk.transport import Transport
1415
from sentry_sdk.tracing import Transaction
1516

@@ -20,7 +21,7 @@ def test_basic(sentry_init, capture_events, sample_rate):
2021
events = capture_events()
2122

2223
with start_transaction(name="hi") as transaction:
23-
transaction.set_status("ok")
24+
transaction.set_status(SPANSTATUS.OK)
2425
with pytest.raises(ZeroDivisionError):
2526
with start_span(op="foo", description="foodesc"):
2627
1 / 0

0 commit comments

Comments
 (0)