diff --git a/sentry_sdk/integrations/arq.py b/sentry_sdk/integrations/arq.py index 5aa0ba7302..c26db4520c 100644 --- a/sentry_sdk/integrations/arq.py +++ b/sentry_sdk/integrations/arq.py @@ -1,5 +1,7 @@ import sys +from opentelemetry.trace.status import StatusCode + import sentry_sdk from sentry_sdk.consts import OP, SPANSTATUS from sentry_sdk.integrations import DidNotEnable, Integration @@ -116,7 +118,10 @@ async def _sentry_run_job(self, job_id, score): origin=ArqIntegration.origin, ) as span: return_value = await old_run_job(self, job_id, score) - span.set_status(SPANSTATUS.OK) + + if span.status is None: + span.set_status(SPANSTATUS.OK) + return return_value Worker.run_job = _sentry_run_job diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 7686dcf052..a0b9439dc8 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -1583,6 +1583,25 @@ def set_attribute(self, key, value): self._otel_span.set_attribute(key, _serialize_span_attribute(value)) + @property + def status(self): + # type: () -> Optional[str] + """ + Return the Sentry `SPANSTATUS` corresponding to the underlying OTel status. + Because differences in possible values in OTel `StatusCode` and + Sentry `SPANSTATUS` it can not be guaranteed that the status + set in `set_status()` will be the same as the one returned here. + """ + if not hasattr(self._otel_span, "status"): + return None + + if self._otel_span.status.status_code == StatusCode.UNSET: + return None + elif self._otel_span.status.status_code == StatusCode.OK: + return SPANSTATUS.OK + else: + return SPANSTATUS.UNKNOWN_ERROR + def set_status(self, status): # type: (str) -> None if status == SPANSTATUS.OK: diff --git a/tests/integrations/opentelemetry/test_potel.py b/tests/integrations/opentelemetry/test_potel.py index 39c48f8cc8..2d1d66c6d0 100644 --- a/tests/integrations/opentelemetry/test_potel.py +++ b/tests/integrations/opentelemetry/test_potel.py @@ -2,6 +2,7 @@ from opentelemetry import trace import sentry_sdk +from sentry_sdk.consts import SPANSTATUS from tests.conftest import ApproxDict @@ -331,3 +332,35 @@ def test_potel_span_root_span_references(): with sentry_sdk.start_span(description="http") as http_span: assert not http_span.is_root_span assert http_span.root_span == request_span + + +@pytest.mark.parametrize( + "status_in,status_out", + [ + (None, None), + ("", SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.OK, SPANSTATUS.OK), + (SPANSTATUS.ABORTED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.ALREADY_EXISTS, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.CANCELLED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.DATA_LOSS, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.DEADLINE_EXCEEDED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.FAILED_PRECONDITION, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.INTERNAL_ERROR, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.INVALID_ARGUMENT, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.NOT_FOUND, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.OUT_OF_RANGE, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.PERMISSION_DENIED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.RESOURCE_EXHAUSTED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.UNAUTHENTICATED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.UNAVAILABLE, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.UNIMPLEMENTED, SPANSTATUS.UNKNOWN_ERROR), + (SPANSTATUS.UNKNOWN_ERROR, SPANSTATUS.UNKNOWN_ERROR), + ], +) +def test_potel_span_status(status_in, status_out): + span = sentry_sdk.start_span(name="test") + if status_in is not None: + span.set_status(status_in) + + assert span.status == status_out