Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 1 addition & 71 deletions src/sentry/api/endpoints/organization_trace_item_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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,
)
Expand All @@ -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,
):
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
36 changes: 7 additions & 29 deletions src/sentry/api/endpoints/project_trace_item_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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,
),
Expand Down
2 changes: 0 additions & 2 deletions src/sentry/features/temporary.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 0 additions & 5 deletions src/sentry/search/eap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 0 additions & 1 deletion src/sentry/seer/endpoints/seer_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)

Expand Down
10 changes: 2 additions & 8 deletions tests/sentry/api/endpoints/test_project_trace_item_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 == [
Expand All @@ -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 == []
Expand Down Expand Up @@ -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,
)

Expand All @@ -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",
Expand All @@ -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
Expand All @@ -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]
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -748,102 +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,
"organizations:performance-sentry-conventions-fields": 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,
Expand Down
Loading
Loading