diff --git a/sentry_sdk/integrations/opentelemetry/potel_span_processor.py b/sentry_sdk/integrations/opentelemetry/potel_span_processor.py index 9604676dce..cddaf24ab2 100644 --- a/sentry_sdk/integrations/opentelemetry/potel_span_processor.py +++ b/sentry_sdk/integrations/opentelemetry/potel_span_processor.py @@ -116,12 +116,12 @@ def _root_span_to_transaction_event(self, span): span_id = format_span_id(span.context.span_id) parent_span_id = format_span_id(span.parent.span_id) if span.parent else None - (op, description, status, _) = extract_span_data(span) + (op, description, status, _, origin) = extract_span_data(span) trace_context = { "trace_id": trace_id, "span_id": span_id, - "origin": SPAN_ORIGIN, + "origin": origin, "op": op, "status": status, } # type: dict[str, Any] @@ -160,17 +160,17 @@ def _span_to_json(self, span): span_id = format_span_id(span.context.span_id) parent_span_id = format_span_id(span.parent.span_id) if span.parent else None - (op, description, status, _) = extract_span_data(span) + (op, description, status, _, origin) = extract_span_data(span) span_json = { "trace_id": trace_id, "span_id": span_id, - "origin": SPAN_ORIGIN, "op": op, "description": description, "status": status, "start_timestamp": convert_otel_timestamp(span.start_time), "timestamp": convert_otel_timestamp(span.end_time), + "origin": origin or SPAN_ORIGIN, } # type: dict[str, Any] if parent_span_id: diff --git a/sentry_sdk/integrations/opentelemetry/span_processor.py b/sentry_sdk/integrations/opentelemetry/span_processor.py index 7671c798c8..2140b0e70b 100644 --- a/sentry_sdk/integrations/opentelemetry/span_processor.py +++ b/sentry_sdk/integrations/opentelemetry/span_processor.py @@ -259,7 +259,7 @@ def _update_span_with_otel_data(self, sentry_span, otel_span): for key, val in otel_span.attributes.items(): sentry_span.set_data(key, val) - (op, description, status, http_status) = extract_span_data(otel_span) + (op, description, status, http_status, _) = extract_span_data(otel_span) sentry_span.op = op sentry_span.description = description @@ -270,7 +270,7 @@ def _update_span_with_otel_data(self, sentry_span, otel_span): def _update_transaction_with_otel_data(self, sentry_span, otel_span): # type: (SentrySpan, OTelSpan) -> None - (op, _, status, http_status) = extract_span_data(otel_span) + (op, _, status, http_status, _) = extract_span_data(otel_span) sentry_span.op = op if http_status: diff --git a/sentry_sdk/integrations/opentelemetry/utils.py b/sentry_sdk/integrations/opentelemetry/utils.py index df668799cf..ecb1852404 100644 --- a/sentry_sdk/integrations/opentelemetry/utils.py +++ b/sentry_sdk/integrations/opentelemetry/utils.py @@ -6,6 +6,7 @@ from opentelemetry.sdk.trace import ReadableSpan from sentry_sdk.consts import SPANSTATUS from sentry_sdk.tracing import get_span_status_from_http_code +from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute from urllib3.util import parse_url as urlparse from sentry_sdk.utils import Dsn @@ -77,13 +78,17 @@ def convert_otel_timestamp(time): def extract_span_data(span): - # type: (ReadableSpan) -> tuple[str, str, Optional[str], Optional[int]] + # type: (ReadableSpan) -> tuple[str, str, Optional[str], Optional[int], Optional[str]] op = span.name description = span.name status, http_status = extract_span_status(span) + origin = None if span.attributes is None: - return (op, description, status, http_status) + return (op, description, status, http_status, origin) + + origin = span.attributes.get(SentrySpanAttribute.ORIGIN) + description = span.attributes.get(SentrySpanAttribute.DESCRIPTION) or description http_method = span.attributes.get(SpanAttributes.HTTP_METHOD) http_method = cast("Optional[str]", http_method) @@ -96,26 +101,21 @@ def extract_span_data(span): rpc_service = span.attributes.get(SpanAttributes.RPC_SERVICE) if rpc_service: - return ("rpc", description, status, http_status) + return ("rpc", description, status, http_status, origin) messaging_system = span.attributes.get(SpanAttributes.MESSAGING_SYSTEM) if messaging_system: - return ("message", description, status, http_status) + return ("message", description, status, http_status, origin) faas_trigger = span.attributes.get(SpanAttributes.FAAS_TRIGGER) if faas_trigger: - return ( - str(faas_trigger), - description, - status, - http_status, - ) + return (str(faas_trigger), description, status, http_status, origin) - return (op, description, status, http_status) + return (op, description, status, http_status, origin) def span_data_for_http_method(span): - # type: (ReadableSpan) -> tuple[str, str, Optional[str], Optional[int]] + # type: (ReadableSpan) -> tuple[str, str, Optional[str], Optional[int], Optional[str]] span_attributes = span.attributes or {} op = "http" @@ -151,11 +151,13 @@ def span_data_for_http_method(span): status, http_status = extract_span_status(span) - return (op, description, status, http_status) + origin = span_attributes.get(SentrySpanAttribute.ORIGIN) + + return (op, description, status, http_status, origin) def span_data_for_db_query(span): - # type: (ReadableSpan) -> tuple[str, str, Optional[str], Optional[int]] + # type: (ReadableSpan) -> tuple[str, str, Optional[str], Optional[int], Optional[str]] span_attributes = span.attributes or {} op = "db" @@ -164,8 +166,9 @@ def span_data_for_db_query(span): statement = cast("Optional[str]", statement) description = statement or span.name + origin = span_attributes.get(SentrySpanAttribute.ORIGIN) - return (op, description, None, None) + return (op, description, None, None, origin) def extract_span_status(span): diff --git a/tests/integrations/opentelemetry/test_potel.py b/tests/integrations/opentelemetry/test_potel.py index 2e094b41b5..5e44cc3888 100644 --- a/tests/integrations/opentelemetry/test_potel.py +++ b/tests/integrations/opentelemetry/test_potel.py @@ -41,7 +41,7 @@ def test_root_span_transaction_payload_started_with_otel_only(capture_envelopes) trace_context = contexts["trace"] assert "trace_id" in trace_context assert "span_id" in trace_context - assert trace_context["origin"] == "auto.otel" + assert trace_context["origin"] == "manual" assert trace_context["op"] == "request" assert trace_context["status"] == "ok" @@ -62,7 +62,7 @@ def test_child_span_payload_started_with_otel_only(capture_envelopes): assert span["op"] == "db" assert span["description"] == "db" - assert span["origin"] == "auto.otel" + assert span["origin"] == "manual" assert span["status"] == "ok" assert span["span_id"] is not None assert span["trace_id"] == payload["contexts"]["trace"]["trace_id"] @@ -124,7 +124,7 @@ def test_root_span_transaction_payload_started_with_sentry_only(capture_envelope trace_context = contexts["trace"] assert "trace_id" in trace_context assert "span_id" in trace_context - assert trace_context["origin"] == "auto.otel" + assert trace_context["origin"] == "manual" assert trace_context["op"] == "request" assert trace_context["status"] == "ok" @@ -145,7 +145,7 @@ def test_child_span_payload_started_with_sentry_only(capture_envelopes): assert span["op"] == "db" assert span["description"] == "db" - assert span["origin"] == "auto.otel" + assert span["origin"] == "manual" assert span["status"] == "ok" assert span["span_id"] is not None assert span["trace_id"] == payload["contexts"]["trace"]["trace_id"] diff --git a/tests/integrations/opentelemetry/test_utils.py b/tests/integrations/opentelemetry/test_utils.py index ceb58a58ef..66ffd7898a 100644 --- a/tests/integrations/opentelemetry/test_utils.py +++ b/tests/integrations/opentelemetry/test_utils.py @@ -23,6 +23,7 @@ "description": "OTel Span Blank", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -36,6 +37,7 @@ "description": "OTel Span RPC", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -49,6 +51,7 @@ "description": "OTel Span Messaging", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -62,6 +65,7 @@ "description": "OTel Span FaaS", "status": "ok", "http_status_code": None, + "origin": None, }, ), ], @@ -72,12 +76,13 @@ def test_extract_span_data(name, status, attributes, expected): otel_span.status = Status(StatusCode.UNSET) otel_span.attributes = attributes - op, description, status, http_status_code = extract_span_data(otel_span) + op, description, status, http_status_code, origin = extract_span_data(otel_span) result = { "op": op, "description": description, "status": status, "http_status_code": http_status_code, + "origin": origin, } assert result == expected @@ -99,6 +104,7 @@ def test_extract_span_data(name, status, attributes, expected): "description": "GET", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -113,6 +119,7 @@ def test_extract_span_data(name, status, attributes, expected): "description": "GET /target", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -127,6 +134,7 @@ def test_extract_span_data(name, status, attributes, expected): "description": "GET example.com", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -142,6 +150,7 @@ def test_extract_span_data(name, status, attributes, expected): "description": "GET /target", "status": "ok", "http_status_code": None, + "origin": None, }, ), ( @@ -156,6 +165,7 @@ def test_extract_span_data(name, status, attributes, expected): "description": "GET https://example.com/bla/", "status": "ok", "http_status_code": None, + "origin": None, }, ), ], @@ -166,12 +176,15 @@ def test_span_data_for_http_method(kind, status, attributes, expected): otel_span.status = status otel_span.attributes = attributes - op, description, status, http_status_code = span_data_for_http_method(otel_span) + op, description, status, http_status_code, origin = span_data_for_http_method( + otel_span + ) result = { "op": op, "description": description, "status": status, "http_status_code": http_status_code, + "origin": origin, } assert result == expected @@ -181,19 +194,21 @@ def test_span_data_for_db_query(): otel_span.name = "OTel Span" otel_span.attributes = {} - op, description, status, http_status = span_data_for_db_query(otel_span) + op, description, status, http_status, origin = span_data_for_db_query(otel_span) assert op == "db" assert description == "OTel Span" assert status is None assert http_status is None + assert origin is None otel_span.attributes = {"db.statement": "SELECT * FROM table;"} - op, description, status, http_status = span_data_for_db_query(otel_span) + op, description, status, http_status, origin = span_data_for_db_query(otel_span) assert op == "db" assert description == "SELECT * FROM table;" assert status is None assert http_status is None + assert origin is None @pytest.mark.parametrize(