diff --git a/sentry_sdk/opentelemetry/utils.py b/sentry_sdk/opentelemetry/utils.py index 83814d2528..a26778a150 100644 --- a/sentry_sdk/opentelemetry/utils.py +++ b/sentry_sdk/opentelemetry/utils.py @@ -219,53 +219,50 @@ def infer_description(span: ReadableSpan) -> Optional[str]: def extract_span_status(span: ReadableSpan) -> tuple[Optional[str], Optional[int]]: + """ + Extract a reasonable Sentry SPANSTATUS and a HTTP status code from the otel span. + OKs are simply OKs. + ERRORs first try to map to HTTP/GRPC statuses via attributes otherwise fallback + on the description if it is a valid status for Sentry. + In the final UNSET case, we try to infer HTTP/GRPC. + """ span_attributes = span.attributes or {} - status = span.status or None - - if status: - inferred_status, http_status = infer_status_from_attributes(span_attributes) - - if status.status_code == StatusCode.OK: - return (SPANSTATUS.OK, http_status) - elif status.status_code == StatusCode.ERROR: - if status.description is None: - if inferred_status: - return (inferred_status, http_status) - - if http_status is not None: - return (inferred_status, http_status) - - if ( - status.description is not None - and status.description in GRPC_ERROR_MAP.values() - ): - return (status.description, None) - else: - return (SPANSTATUS.UNKNOWN_ERROR, None) - - inferred_status, http_status = infer_status_from_attributes(span_attributes) - if inferred_status: - return (inferred_status, http_status) - - if status and status.status_code == StatusCode.UNSET: - return (None, None) + status = span.status + http_status = get_http_status_code(span_attributes) + final_status = None + + if status.status_code == StatusCode.OK: + final_status = SPANSTATUS.OK + elif status.status_code == StatusCode.ERROR: + inferred_status = infer_status_from_attributes(span_attributes, http_status) + + if inferred_status is not None: + final_status = inferred_status + elif ( + status.description is not None + and status.description in GRPC_ERROR_MAP.values() + ): + final_status = status.description + else: + final_status = SPANSTATUS.UNKNOWN_ERROR else: - return (SPANSTATUS.UNKNOWN_ERROR, None) + # UNSET case + final_status = infer_status_from_attributes(span_attributes, http_status) + return (final_status, http_status) -def infer_status_from_attributes( - span_attributes: Mapping[str, Any], -) -> tuple[Optional[str], Optional[int]]: - http_status = get_http_status_code(span_attributes) +def infer_status_from_attributes( + span_attributes: Mapping[str, Any], http_status: Optional[int] +) -> Optional[str]: if http_status: - return (get_span_status_from_http_code(http_status), http_status) + return get_span_status_from_http_code(http_status) grpc_status = span_attributes.get(SpanAttributes.RPC_GRPC_STATUS_CODE) if grpc_status: - return (GRPC_ERROR_MAP.get(str(grpc_status), SPANSTATUS.UNKNOWN_ERROR), None) + return GRPC_ERROR_MAP.get(str(grpc_status), SPANSTATUS.UNKNOWN_ERROR) - return (None, None) + return None def get_http_status_code(span_attributes: Mapping[str, Any]) -> Optional[int]: diff --git a/tests/opentelemetry/test_utils.py b/tests/opentelemetry/test_utils.py index 9997fc27cc..6313e6ccc1 100644 --- a/tests/opentelemetry/test_utils.py +++ b/tests/opentelemetry/test_utils.py @@ -219,31 +219,6 @@ def test_span_data_for_db_query(): @pytest.mark.parametrize( "kind, status, attributes, expected", [ - ( - SpanKind.CLIENT, - None, # None means unknown error - { - "http.method": "POST", - "http.route": "/some/route", - }, - { - "status": "unknown_error", - "http_status_code": None, - }, - ), - ( - SpanKind.CLIENT, - None, - { - "http.method": "POST", - "http.route": "/some/route", - "http.status_code": 502, # Take this status in case of None status - }, - { - "status": "internal_error", - "http_status_code": 502, - }, - ), ( SpanKind.SERVER, Status(StatusCode.UNSET), @@ -269,23 +244,6 @@ def test_span_data_for_db_query(): "http_status_code": 502, }, ), - ( - SpanKind.SERVER, - None, - { - "http.method": "POST", - "http.route": "/some/route", - "http.status_code": 502, - "http.response.status_code": 503, # this takes precedence over deprecated http.status_code - }, - { - "status": "unavailable", - "http_status_code": 503, - # old otel versions won't take the new attribute into account - "status_old": "internal_error", - "http_status_code_old": 502, - }, - ), ( SpanKind.SERVER, Status(StatusCode.UNSET),