Skip to content

Commit f086126

Browse files
committed
fix(eap): Handle contexts in trace-item attributes
- Take contexts into account for the trace item attributes so we return them as possible fields
1 parent c97b218 commit f086126

File tree

5 files changed

+89
-15
lines changed

5 files changed

+89
-15
lines changed

src/sentry/api/endpoints/organization_trace_item_attributes.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
TraceItemType as ProtoTraceItemType,
2121
)
2222
from sentry_protos.snuba.v1.trace_item_attribute_pb2 import AttributeKey
23-
from sentry_protos.snuba.v1.trace_item_filter_pb2 import ExistsFilter, OrFilter, TraceItemFilter
23+
from sentry_protos.snuba.v1.trace_item_filter_pb2 import (
24+
ExistsFilter,
25+
OrFilter,
26+
TraceItemFilter,
27+
)
2428

2529
from sentry import features, options
2630
from sentry.api.api_owners import ApiOwner
@@ -54,7 +58,11 @@
5458
from sentry.search.eap.resolver import SearchResolver
5559
from sentry.search.eap.spans.definitions import SPAN_DEFINITIONS
5660
from sentry.search.eap.trace_metrics.definitions import TRACE_METRICS_DEFINITIONS
57-
from sentry.search.eap.types import SearchResolverConfig, SupportedTraceItemType
61+
from sentry.search.eap.types import (
62+
AttributeSourceType,
63+
SearchResolverConfig,
64+
SupportedTraceItemType,
65+
)
5866
from sentry.search.eap.utils import (
5967
can_expose_attribute,
6068
get_secondary_aliases,
@@ -79,6 +87,10 @@
7987
POSSIBLE_ATTRIBUTE_TYPES = ["string", "number", "boolean"]
8088

8189

90+
class ProxyResolvedAttribute(ResolvedAttribute):
91+
pass
92+
93+
8294
class TraceItemAttributeKey(TypedDict):
8395
key: str
8496
name: str
@@ -218,6 +230,7 @@ def as_attribute_key(
218230
name: str,
219231
attr_type: Literal["string", "number", "boolean"],
220232
item_type: SupportedTraceItemType,
233+
is_proxy: bool = False,
221234
) -> TraceItemAttributeKey:
222235
public_key, public_name, attribute_source = translate_internal_to_public_alias(
223236
name, attr_type, item_type
@@ -237,7 +250,11 @@ def as_attribute_key(
237250
public_name = name
238251

239252
serialized_source: dict[str, str | bool] = {
240-
"source_type": attribute_source["source_type"].value
253+
"source_type": (
254+
attribute_source["source_type"].value
255+
if not is_proxy
256+
else AttributeSourceType.SENTRY.value
257+
)
241258
}
242259
if attribute_source.get("is_transformed_alias"):
243260
serialized_source["is_transformed_alias"] = True
@@ -378,15 +395,33 @@ def query_trace_attributes(
378395
all_aliased_attributes = []
379396
# our aliases don't exist in the db, so filter over our aliases
380397
# virtually page through defined aliases before we hit the db
381-
if substring_match and offset <= len(column_definitions.columns):
382-
for index, column in enumerate(column_definitions.columns.values()):
398+
if substring_match and offset <= len(column_definitions.columns) + len(
399+
column_definitions.contexts
400+
):
401+
for column in column_definitions.columns.values():
383402
if (
384403
column.proto_type == attr_type
385404
and substring_match in column.public_alias
386405
and not column.secondary_alias
387406
and not column.private
388407
):
389408
all_aliased_attributes.append(column)
409+
for (
410+
public_label,
411+
virtual_context,
412+
) in column_definitions.contexts.items():
413+
if (
414+
substring_match in public_label
415+
and virtual_context.search_type is not None
416+
and constants.TYPE_MAP[virtual_context.search_type] == attr_type
417+
):
418+
all_aliased_attributes.append(
419+
ProxyResolvedAttribute(
420+
public_alias=public_label,
421+
internal_name=public_label,
422+
search_type=virtual_context.search_type,
423+
)
424+
)
390425
aliased_attributes = all_aliased_attributes[offset : offset + limit]
391426
with sentry_sdk.start_span(op="query", name="attribute_names") as span:
392427
if len(aliased_attributes) < limit - 1:
@@ -433,7 +468,7 @@ def serialize_trace_attributes_using_sentry_conventions(
433468
trace_item_type: SupportedTraceItemType,
434469
include_internal: bool,
435470
substring_match: str,
436-
aliased_attributes: list[ResolvedAttribute],
471+
aliased_attributes: list[ResolvedAttribute | ProxyResolvedAttribute],
437472
) -> list[TraceItemAttributeKey]:
438473
attribute_keys = {}
439474
for attribute in rpc_response.attributes:
@@ -462,6 +497,7 @@ def serialize_trace_attributes_using_sentry_conventions(
462497
aliased_attr.internal_name,
463498
attribute_type,
464499
trace_item_type,
500+
is_proxy=isinstance(aliased_attr, ProxyResolvedAttribute),
465501
)
466502
attribute_keys[attr_key["name"]] = attr_key
467503

@@ -476,7 +512,7 @@ def serialize_trace_attributes(
476512
trace_item_type: SupportedTraceItemType,
477513
include_internal: bool,
478514
substring_match: str,
479-
aliased_attributes: list[ResolvedAttribute],
515+
aliased_attributes: list[ResolvedAttribute | ProxyResolvedAttribute],
480516
) -> list[TraceItemAttributeKey]:
481517
attributes = list(
482518
filter(
@@ -513,6 +549,7 @@ def serialize_trace_attributes(
513549
aliased_attr.internal_name,
514550
attribute_type,
515551
trace_item_type,
552+
is_proxy=isinstance(aliased_attr, ProxyResolvedAttribute),
516553
)
517554
attributes.append(attr_key)
518555
return attributes

src/sentry/search/eap/columns.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ class AttributeArgumentDefinition(BaseArgumentDefinition):
158158
@dataclass
159159
class VirtualColumnDefinition:
160160
constructor: Callable[[SnubaParams, Any], VirtualColumnContext]
161+
# Need a type for the attributes endpoint
162+
search_type: constants.SearchType
161163
# Allows additional processing to the term after its been resolved
162164
term_resolver: (
163165
Callable[
@@ -255,7 +257,9 @@ class ResolvedTraceMetricAggregate(ResolvedFunction):
255257
trace_filter: TraceItemFilter | None
256258

257259
@property
258-
def proto_definition(self) -> AttributeAggregation | AttributeConditionalAggregation:
260+
def proto_definition(
261+
self,
262+
) -> AttributeAggregation | AttributeConditionalAggregation:
259263
if self.trace_metric is None and self.trace_filter is None:
260264
return AttributeAggregation(
261265
aggregate=self.internal_name,
@@ -460,9 +464,11 @@ def resolve(
460464
),
461465
key=cast(AttributeKey, resolved_attribute),
462466
trace_metric=trace_metric,
463-
trace_filter=cast(TraceItemFilter, resolved_arguments[0])
464-
if isinstance(self, ConditionalTraceMetricAggregateDefinition)
465-
else None,
467+
trace_filter=(
468+
cast(TraceItemFilter, resolved_arguments[0])
469+
if isinstance(self, ConditionalTraceMetricAggregateDefinition)
470+
else None
471+
),
466472
)
467473

468474

@@ -674,7 +680,9 @@ class ColumnDefinitions:
674680
column_to_alias: Callable[[str], str | None] | None
675681

676682

677-
def attribute_key_to_tuple(attribute_key: AttributeKey) -> tuple[str, AttributeKey.Type.ValueType]:
683+
def attribute_key_to_tuple(
684+
attribute_key: AttributeKey,
685+
) -> tuple[str, AttributeKey.Type.ValueType]:
678686
return (attribute_key.name, attribute_key.type)
679687

680688

@@ -731,9 +739,11 @@ def extract_trace_metric_aggregate_arguments(
731739
return TraceMetric(
732740
metric_name=cast(str, resolved_arguments[offset + 1]),
733741
metric_type=cast(TraceMetricType, resolved_arguments[offset + 2]),
734-
metric_unit=None
735-
if resolved_arguments[offset + 3] == "-"
736-
else cast(str, resolved_arguments[offset + 3]),
742+
metric_unit=(
743+
None
744+
if resolved_arguments[offset + 3] == "-"
745+
else cast(str, resolved_arguments[offset + 3])
746+
),
737747
)
738748
elif all(resolved_argument == "" for resolved_argument in resolved_arguments[offset + 1 :]):
739749
# no metrics were specified, assume we query all metrics

src/sentry/search/eap/common_columns.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
constructor=project_context_constructor(key),
1313
term_resolver=project_term_resolver,
1414
filter_column="project.id",
15+
search_type="string",
1516
)
1617
for key in constants.PROJECT_FIELDS
1718
}

src/sentry/search/eap/spans/attributes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,14 +682,17 @@ def is_starred_segment_context_constructor(
682682
# TODO: need to change this so the VCC is using it too, but would require rewriting the term_resolver
683683
default_value="Unknown",
684684
sort_column="sentry.device.class",
685+
search_type="string",
685686
),
686687
"span.module": VirtualColumnDefinition(
687688
constructor=module_context_constructor,
689+
search_type="string",
688690
),
689691
"is_starred_transaction": VirtualColumnDefinition(
690692
constructor=is_starred_segment_context_constructor,
691693
default_value="false",
692694
processor=lambda x: True if x == "true" else False,
695+
search_type="boolean",
693696
),
694697
**project_virtual_contexts(),
695698
}

tests/snuba/api/endpoints/test_organization_trace_item_attributes.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,29 @@ def test_aliased_attribute(self) -> None:
980980
keys = {item["key"] for item in response.data}
981981
assert len(keys) == 0
982982

983+
def test_aliased_attribute_project(self) -> None:
984+
span1 = self.create_span(
985+
{"sentry_tags": {"op": "foo"}}, start_ts=before_now(days=0, minutes=10)
986+
)
987+
span2 = self.create_span(
988+
{"sentry_tags": {"op": "bar"}}, start_ts=before_now(days=0, minutes=10)
989+
)
990+
self.store_spans([span1, span2])
991+
992+
response = self.do_request(query={"attributeType": "string"})
993+
assert response.status_code == 200, response.content
994+
995+
keys = {item["key"] for item in response.data}
996+
assert len(keys) > 1
997+
assert "project" in keys
998+
999+
response = self.do_request(query={"attributeType": "string", "substringMatch": "pro"})
1000+
assert response.status_code == 200, response.content
1001+
1002+
keys = {item["key"] for item in response.data}
1003+
assert len(keys) == 5
1004+
assert "project" in keys
1005+
9831006
def test_aliased_attribute_with_paging(self) -> None:
9841007
span1 = self.create_span(
9851008
{"tags": {"tag.op": "foo"}}, start_ts=before_now(days=0, minutes=10)

0 commit comments

Comments
 (0)