From 2f397f5a96c1da047d4cdfa27a88633c5d398790 Mon Sep 17 00:00:00 2001 From: Matt Quinn Date: Thu, 28 May 2026 11:53:58 -0400 Subject: [PATCH 1/2] ref(trace-items): Remove `performance-sentry-conventions-fields` This work was incomplete and unused (no users had the flag enabled, even internally). Remove it for now. If it ever needs to come back we'll rescue this implementation or reimplement it. Removing the flag definition is safe because the reference in Flagpole was [already removed](https://github.com/getsentry/sentry-options-automator/pull/7963). --- .../organization_trace_item_attributes.py | 72 +------------------ .../endpoints/project_trace_item_details.py | 36 ++-------- src/sentry/features/temporary.py | 2 - src/sentry/search/eap/utils.py | 5 -- src/sentry/seer/endpoints/seer_rpc.py | 1 - .../test_project_trace_item_details.py | 10 +-- ...test_organization_trace_item_attributes.py | 1 - .../test_project_trace_item_details.py | 1 - 8 files changed, 10 insertions(+), 118 deletions(-) diff --git a/src/sentry/api/endpoints/organization_trace_item_attributes.py b/src/sentry/api/endpoints/organization_trace_item_attributes.py index 0839655d21d0..a30503db1b90 100644 --- a/src/sentry/api/endpoints/organization_trace_item_attributes.py +++ b/src/sentry/api/endpoints/organization_trace_item_attributes.py @@ -68,7 +68,6 @@ get_secondary_aliases, is_sentry_convention_replacement_attribute, translate_internal_to_public_alias, - translate_to_sentry_conventions, ) from sentry.search.events.constants import ( RELEASE_STAGE_ALIAS, @@ -295,14 +294,6 @@ def get(self, request: Request, organization: Organization) -> Response: paginator=ChainPaginator([]), ) - use_sentry_conventions = features.has( - "organizations:performance-sentry-conventions-fields", - organization, - actor=request.user, - ) - - sentry_sdk.set_tag("feature.use_sentry_conventions", use_sentry_conventions) - serialized = serializer.validated_data substring_match = serialized.get("substring_match", "") query_string = serialized.get("query") @@ -357,7 +348,6 @@ def data_fn(offset: int, limit: int) -> list[TraceItemAttributeKey]: substring_match, attribute_type, column_definitions, - use_sentry_conventions, trace_item_type, include_internal, ) @@ -384,7 +374,6 @@ def query_trace_attributes( substring_match: str, attribute_type: Literal["string", "number", "boolean"], column_definitions: ColumnDefinitions, - use_sentry_conventions: bool, trace_item_type: SupportedTraceItemType, include_internal: bool, ): @@ -461,12 +450,7 @@ def query_trace_attributes( rpc_response = TraceItemAttributeNamesResponse() with sentry_sdk.start_span(op="query", name="serialize") as span: - if use_sentry_conventions: - serialize_function = self.serialize_trace_attributes_using_sentry_conventions - else: - serialize_function = self.serialize_trace_attributes - - attributes = serialize_function( + attributes = self.serialize_trace_attributes( rpc_response, attribute_type, trace_item_type, @@ -481,60 +465,6 @@ def query_trace_attributes( span.set_data("attribute_type", attribute_type) return attributes - def serialize_trace_attributes_using_sentry_conventions( - self, - rpc_response: TraceItemAttributeNamesResponse, - attribute_type: Literal["string", "number", "boolean"], - trace_item_type: SupportedTraceItemType, - include_internal: bool, - substring_match: str, - aliased_attributes: list[ResolvedAttribute | ProxyResolvedAttribute], - exclude_attributes: list[ResolvedAttribute | ProxyResolvedAttribute], - ) -> list[TraceItemAttributeKey]: - attribute_keys = {} - for attribute in rpc_response.attributes: - if attribute.name and can_expose_attribute( - attribute.name, - trace_item_type, - include_internal=include_internal, - ): - attr_key = as_attribute_key( - attribute.name, - attribute_type, - trace_item_type, - ) - public_alias = attr_key["name"] - replacement = translate_to_sentry_conventions(public_alias, trace_item_type) - if public_alias != replacement: - attr_key = as_attribute_key( - replacement, - attribute_type, - trace_item_type, - ) - - attribute_keys[attr_key["name"]] = attr_key - for aliased_attr in exclude_attributes: - attr_key = as_attribute_key( - aliased_attr.internal_name, - attribute_type, - trace_item_type, - is_proxy=isinstance(aliased_attr, ProxyResolvedAttribute), - ) - if attr_key["name"] in attribute_keys: - del attribute_keys[attr_key["name"]] - for aliased_attr in aliased_attributes: - attr_key = as_attribute_key( - aliased_attr.internal_name, - attribute_type, - trace_item_type, - is_proxy=isinstance(aliased_attr, ProxyResolvedAttribute), - ) - attribute_keys[attr_key["name"]] = attr_key - - attributes = list(attribute_keys.values()) - sentry_sdk.set_context("api_response", {"attributes": attributes}) - return attributes - def serialize_trace_attributes( self, rpc_response: TraceItemAttributeNamesResponse, diff --git a/src/sentry/api/endpoints/project_trace_item_details.py b/src/sentry/api/endpoints/project_trace_item_details.py index 6efbba3419db..2969bb8ef075 100644 --- a/src/sentry/api/endpoints/project_trace_item_details.py +++ b/src/sentry/api/endpoints/project_trace_item_details.py @@ -37,7 +37,6 @@ is_sentry_convention_replacement_attribute, translate_internal_to_public_alias, translate_search_type_for_internal_column, - translate_to_sentry_conventions, ) from sentry.search.utils import InvalidQuery, parse_datetime_string from sentry.snuba.referrer import Referrer @@ -112,12 +111,10 @@ def _get_value_from_attribute( def convert_rpc_attribute_to_json( attributes: list[dict], trace_item_type: SupportedTraceItemType, - use_sentry_conventions: bool = False, include_internal: bool = False, include_arrays: bool = False, ) -> list[TraceItemAttribute]: result: list[TraceItemAttribute] = [] - seen_sentry_conventions: set[str] = set() all_internal_names = {attr["name"] for attr in attributes} for attribute in attributes: @@ -147,17 +144,12 @@ def convert_rpc_attribute_to_json( internal_name, translate_type, trace_item_type ) if not include_arrays: - convention_name = ( - translate_to_sentry_conventions(external_name, trace_item_type) - if use_sentry_conventions and external_name - else external_name - ) if ( translate_type != "string" or not isinstance(output_value, list) - or not convention_name + or not external_name or not is_sentry_convention_replacement_attribute( - convention_name, trace_item_type + external_name, trace_item_type ) ): continue @@ -172,20 +164,12 @@ def convert_rpc_attribute_to_json( internal_name, translate_type, trace_item_type ) - if use_sentry_conventions and external_name: - external_name = translate_to_sentry_conventions(external_name, trace_item_type) - if external_name in seen_sentry_conventions: + if external_name and is_sentry_convention_replacement_attribute( + external_name, trace_item_type + ): + deprecated_names = get_deprecated_source_internal_names(external_name, trace_item_type) + if not deprecated_names.isdisjoint(all_internal_names): continue - seen_sentry_conventions.add(external_name) - else: - if external_name and is_sentry_convention_replacement_attribute( - external_name, trace_item_type - ): - deprecated_names = get_deprecated_source_internal_names( - external_name, trace_item_type - ) - if not deprecated_names.isdisjoint(all_internal_names): - continue if trace_item_type == SupportedTraceItemType.SPANS and internal_name.startswith("sentry."): internal_name = internal_name.replace("sentry.", "", count=1) @@ -434,11 +418,6 @@ def get(request: Request, project: Project, item_id: str) -> Response: resp = MessageToDict(trace_item_details_rpc(req)) - use_sentry_conventions = features.has( - "organizations:performance-sentry-conventions-fields", - project.organization, - actor=request.user, - ) include_arrays = features.has( "organizations:trace-item-details-array-fields", project.organization, @@ -453,7 +432,6 @@ def get(request: Request, project: Project, item_id: str) -> Response: "attributes": convert_rpc_attribute_to_json( resp["attributes"], item_type, - use_sentry_conventions, include_internal=include_internal, include_arrays=include_arrays, ), diff --git a/src/sentry/features/temporary.py b/src/sentry/features/temporary.py index 3bd5327635ed..d3319439e0c8 100644 --- a/src/sentry/features/temporary.py +++ b/src/sentry/features/temporary.py @@ -195,8 +195,6 @@ def register_temporary_features(manager: FeatureManager) -> None: manager.add("organizations:insights-ai-and-mcp-dashboard-migration", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable all registered prebuilt dashboards to be synced to the database manager.add("organizations:dashboards-sync-all-registered-prebuilt-dashboards", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=False) - # Enable sentry convention fields - manager.add("organizations:performance-sentry-conventions-fields", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable Seer Suggestions for Web Vitals Module manager.add("organizations:performance-web-vitals-seer-suggestions", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable the warning banner to inform users of pending deprecation of the transactions dataset diff --git a/src/sentry/search/eap/utils.py b/src/sentry/search/eap/utils.py index 3d05577c8c58..be54d93669b0 100644 --- a/src/sentry/search/eap/utils.py +++ b/src/sentry/search/eap/utils.py @@ -326,8 +326,3 @@ def get_deprecated_source_internal_names( replacement: str, item_type: SupportedTraceItemType ) -> set[str]: return SENTRY_CONVENTIONS_REVERSE_REPLACEMENT_MAP.get(item_type, {}).get(replacement, set()) - - -def translate_to_sentry_conventions(public_alias: str, item_type: SupportedTraceItemType) -> str: - mapping = SENTRY_CONVENTIONS_REPLACEMENT_MAPPINGS.get(item_type, {}) - return mapping.get(public_alias, public_alias) diff --git a/src/sentry/seer/endpoints/seer_rpc.py b/src/sentry/seer/endpoints/seer_rpc.py index 54133bedb784..d6c355cacdb2 100644 --- a/src/sentry/seer/endpoints/seer_rpc.py +++ b/src/sentry/seer/endpoints/seer_rpc.py @@ -520,7 +520,6 @@ def get_attributes_for_span( attributes = convert_rpc_attribute_to_json( response_dict.get("attributes", []), SupportedTraceItemType.SPANS, - use_sentry_conventions=False, include_internal=False, ) diff --git a/tests/sentry/api/endpoints/test_project_trace_item_details.py b/tests/sentry/api/endpoints/test_project_trace_item_details.py index 397207da0577..d3f3218f7e81 100644 --- a/tests/sentry/api/endpoints/test_project_trace_item_details.py +++ b/tests/sentry/api/endpoints/test_project_trace_item_details.py @@ -13,7 +13,6 @@ def test_convert_rpc_attribute_to_json_serializes_known_string_array_without_arr } ], SupportedTraceItemType.SPANS, - use_sentry_conventions=True, ) assert result == [ @@ -34,7 +33,6 @@ def test_convert_rpc_attribute_to_json_hides_unknown_array_without_array_flag() } ], SupportedTraceItemType.SPANS, - use_sentry_conventions=True, ) assert result == [] @@ -63,7 +61,6 @@ def test_convert_rpc_attribute_to_json_exposes_array_with_array_flag() -> None: } ], SupportedTraceItemType.SPANS, - use_sentry_conventions=True, include_arrays=True, ) @@ -77,8 +74,8 @@ def test_convert_rpc_attribute_to_json_exposes_array_with_array_flag() -> None: class TestReplacementAttributeFiltering: - """When use_sentry_conventions is off, replacement attributes should only be - hidden if a deprecated source attribute is also present in the response.""" + """Replacement attributes should only be hidden if a deprecated source + attribute is also present in the response.""" @pytest.mark.parametrize( "attr_name,attr_value", @@ -94,7 +91,6 @@ def test_replacement_attribute_shown_when_no_deprecated_source( result = convert_rpc_attribute_to_json( [{"name": attr_name, "value": attr_value}], SupportedTraceItemType.SPANS, - use_sentry_conventions=False, ) assert len(result) == 1 @@ -107,7 +103,6 @@ def test_replacement_attribute_hidden_when_deprecated_source_present(self) -> No {"name": "gen_ai.usage.input_tokens", "value": {"valInt": "42"}}, ], SupportedTraceItemType.SPANS, - use_sentry_conventions=False, ) names = [r["name"] for r in result] @@ -123,7 +118,6 @@ def test_replacement_array_shown_when_no_deprecated_source(self) -> None: } ], SupportedTraceItemType.SPANS, - use_sentry_conventions=False, ) assert len(result) == 1 diff --git a/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py b/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py index 7863f449a9a9..f1442b60b1d8 100644 --- a/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py +++ b/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py @@ -780,7 +780,6 @@ def test_tags_list_sentry_conventions(self) -> None: }, features={ "organizations:visibility-explore-view": True, - "organizations:performance-sentry-conventions-fields": True, }, ) assert response.status_code == 200, response.data diff --git a/tests/snuba/api/endpoints/test_project_trace_item_details.py b/tests/snuba/api/endpoints/test_project_trace_item_details.py index dc29c5c6f079..379ca5a2e918 100644 --- a/tests/snuba/api/endpoints/test_project_trace_item_details.py +++ b/tests/snuba/api/endpoints/test_project_trace_item_details.py @@ -333,7 +333,6 @@ def test_simple_using_spans_item_type_with_sentry_conventions(self) -> None: item_id, features={ "organizations:discover-basic": True, - "organizations:performance-sentry-conventions-fields": True, }, ) assert trace_details_response.status_code == 200, trace_details_response.content From 88e4dbe44111704cad0469e6cd83345a96d606b7 Mon Sep 17 00:00:00 2001 From: Matt Quinn Date: Thu, 28 May 2026 12:57:44 -0400 Subject: [PATCH 2/2] remove tests testing removed flag --- ...test_organization_trace_item_attributes.py | 95 ------------------- .../test_project_trace_item_details.py | 69 -------------- 2 files changed, 164 deletions(-) diff --git a/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py b/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py index f1442b60b1d8..bf84efc03eb5 100644 --- a/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py +++ b/tests/snuba/api/endpoints/test_organization_trace_item_attributes.py @@ -748,101 +748,6 @@ def test_pagination(self) -> None: assert links["previous"]["href"] is not None - def test_tags_list_sentry_conventions(self) -> None: - for tag in [ - "foo", - "bar", - "baz", - "lcp", - "fcp", - "http.decoded_response_content_length", - "http.response_content_length", - "http.response_transfer_size", - "http.response.body.size", - ]: - self.store_segment( - self.project.id, - uuid4().hex, - uuid4().hex, - span_id=uuid4().hex[:16], - organization_id=self.organization.id, - parent_span_id=None, - timestamp=before_now(days=0, minutes=10).replace(microsecond=0), - transaction="foo", - duration=100, - exclusive_time=100, - measurements={tag: 0}, - ) - - response = self.do_request( - { - "attributeType": "number", - }, - features={ - "organizations:visibility-explore-view": True, - }, - ) - assert response.status_code == 200, response.data - assert sorted(response.data, key=itemgetter("key")) == sorted( - [ - { - "key": "tags[bar,number]", - "name": "bar", - "attributeType": "number", - "attributeSource": {"source_type": "user"}, - }, - { - "key": "tags[baz,number]", - "name": "baz", - "attributeType": "number", - "attributeSource": {"source_type": "user"}, - }, - { - "key": "browser.web_vital.fcp.value", - "name": "browser.web_vital.fcp.value", - "attributeType": "number", - "attributeSource": {"source_type": "sentry"}, - }, - { - "key": "tags[foo,number]", - "name": "foo", - "attributeType": "number", - "attributeSource": {"source_type": "user"}, - }, - { - "key": "http.decoded_response_content_length", - "name": "http.decoded_response_content_length", - "attributeType": "number", - "attributeSource": {"source_type": "sentry"}, - }, - { - "key": "http.response.body.size", - "name": "http.response.body.size", - "attributeType": "number", - "attributeSource": {"source_type": "sentry"}, - }, - { - "key": "http.response.size", - "name": "http.response.size", - "attributeType": "number", - "attributeSource": {"source_type": "sentry"}, - }, - { - "key": "browser.web_vital.lcp.value", - "name": "browser.web_vital.lcp.value", - "attributeType": "number", - "attributeSource": {"source_type": "sentry"}, - }, - { - "key": "span.duration", - "name": "span.duration", - "attributeType": "number", - "attributeSource": {"source_type": "sentry"}, - }, - ], - key=itemgetter("key"), - ) - def test_attribute_collision(self) -> None: self.store_segment( self.project.id, diff --git a/tests/snuba/api/endpoints/test_project_trace_item_details.py b/tests/snuba/api/endpoints/test_project_trace_item_details.py index 379ca5a2e918..4ce37d52e3f2 100644 --- a/tests/snuba/api/endpoints/test_project_trace_item_details.py +++ b/tests/snuba/api/endpoints/test_project_trace_item_details.py @@ -313,75 +313,6 @@ def test_simple_using_spans_item_type(self) -> None: == self.one_min_ago.replace(microsecond=0, tzinfo=None).isoformat() + "Z" ) - def test_simple_using_spans_item_type_with_sentry_conventions(self) -> None: - span_1 = self.create_span( - {"description": "foo", "sentry_tags": {"status": "success"}}, - measurements={ - "code.lineno": {"value": 420}, - "http.response_content_length": {"value": 100}, - "http.response.body.size": {"value": 100}, - }, - start_ts=self.one_min_ago, - ) - span_1["trace_id"] = self.trace_uuid - item_id = span_1["span_id"] - - self.store_span(span_1) - - trace_details_response = self.do_request( - "spans", - item_id, - features={ - "organizations:discover-basic": True, - }, - ) - assert trace_details_response.status_code == 200, trace_details_response.content - assert trace_details_response.data["attributes"] == [ - {"name": "is_transaction", "type": "bool", "value": False}, - {"name": "code.lineno", "type": "float", "value": 420.0}, - {"name": "http.response.body.size", "type": "float", "value": 100.0}, - { - "name": "precise.finish_ts", - "type": "float", - "value": pytest.approx(self.one_min_ago.timestamp()), - }, - { - "name": "precise.start_ts", - "type": "float", - "value": pytest.approx(self.one_min_ago.timestamp()), - }, - { - "name": "received", - "type": "float", - "value": pytest.approx(self.one_min_ago.timestamp()), - }, - {"name": "span.self_time", "type": "float", "value": 1000.0}, - {"name": "project_id", "type": "int", "value": str(self.project.id)}, - {"name": "span.duration", "type": "int", "value": "1000"}, - {"name": "parent_span", "type": "str", "value": span_1["parent_span_id"]}, - {"name": "profile.id", "type": "str", "value": span_1["profile_id"]}, - {"name": "sdk.name", "type": "str", "value": "sentry.test.sdk"}, - {"name": "sdk.version", "type": "str", "value": "1.0"}, - { - "name": "sentry.segment.id", - "type": "str", - "value": span_1["segment_id"], - }, - {"name": "span.description", "type": "str", "value": "foo"}, - {"name": "span.status", "type": "str", "value": "success"}, - {"name": "trace", "type": "str", "value": self.trace_uuid}, - { - "name": "transaction.event_id", - "type": "str", - "value": span_1["event_id"], - }, - ] - assert trace_details_response.data["itemId"] == item_id - assert ( - trace_details_response.data["timestamp"] - == self.one_min_ago.replace(microsecond=0, tzinfo=None).isoformat() + "Z" - ) - def test_logs_with_a_meta_key(self) -> None: log = self.create_ourlog( {