Browse Source

fix(starfish): Allow querying of sentry tags (#63643)

- Also updates a few of the known sentry tag fields to query from
sentry_tags automatically
William Mak 1 year ago
parent
commit
1bfacd9f0a

+ 3 - 1
src/sentry/search/events/builder/discover.py

@@ -1260,7 +1260,9 @@ class BaseQueryBuilder:
         # Tags are never null, but promoted tags are columns and so can be null.
         # To handle both cases, use `ifNull` to convert to an empty string and
         # compare so we need to check for empty values.
-        is_tag = isinstance(lhs, Column) and lhs.subscriptable == "tags"
+        is_tag = isinstance(lhs, Column) and (
+            lhs.subscriptable == "tags" or lhs.subscriptable == "sentry_tags"
+        )
         is_context = isinstance(lhs, Column) and lhs.subscriptable == "contexts"
         if is_tag:
             if operator not in ["IN", "NOT IN"] and not isinstance(value, str):

+ 1 - 1
src/sentry/search/events/constants.py

@@ -82,7 +82,7 @@ VITAL_THRESHOLDS: Dict[str, ThresholdDict] = {
     },
 }
 
-TAG_KEY_RE = re.compile(r"^tags\[(?P<tag>.*)\]$")
+TAG_KEY_RE = re.compile(r"^(sentry_tags|tags)\[(?P<tag>.*)\]$")
 # Based on general/src/protocol/tags.rs in relay
 VALID_FIELD_PATTERN = re.compile(r"^[a-zA-Z0-9_.:-]*$")
 

+ 12 - 0
src/sentry/utils/snuba.py

@@ -129,6 +129,12 @@ SPAN_COLUMN_MAP = {
     "transaction.op": "transaction_op",
     "user": "user",
     "profile_id": "profile_id",
+    "transaction.method": "sentry_tags[transaction.method]",
+    "system": "sentry_tags[system]",
+    "release": "sentry_tags[release]",
+    "environment": "sentry_tags[environment]",
+    "device.class": "sentry_tags[device.class]",
+    "category": "sentry_tags[category]",
 }
 
 SPAN_COLUMN_MAP.update(
@@ -1198,6 +1204,12 @@ def resolve_column(dataset) -> Callable[[str], str]:
         if dataset == Dataset.Discover:
             if isinstance(col, (list, tuple)) or col in ("project_id", "group_id"):
                 return col
+        elif (
+            dataset == Dataset.SpansIndexed
+            and isinstance(col, str)
+            and col.startswith("sentry_tags[")
+        ):
+            return col
         else:
             if (
                 col in DATASET_FIELDS[dataset]

+ 51 - 0
tests/snuba/api/endpoints/test_organization_events_span_indexed.py

@@ -37,6 +37,7 @@ class OrganizationEventsSpanIndexedEndpointTest(OrganizationEventsEndpointTestBa
                 "dataset": "spansIndexed",
             }
         )
+
         assert response.status_code == 200, response.content
         data = response.data["data"]
         meta = response.data["meta"]
@@ -44,3 +45,53 @@ class OrganizationEventsSpanIndexedEndpointTest(OrganizationEventsEndpointTestBa
         assert data[0]["description"] == "bar"
         assert data[1]["description"] == "foo"
         assert meta["dataset"] == "spansIndexed"
+
+    def test_sentry_tags_vs_tags(self):
+        self.store_spans(
+            [
+                self.create_span(
+                    {"sentry_tags": {"transaction.method": "foo"}}, start_ts=self.ten_mins_ago
+                ),
+            ]
+        )
+        response = self.do_request(
+            {
+                "field": ["transaction.method", "count()"],
+                "query": "",
+                "orderby": "count()",
+                "project": self.project.id,
+                "dataset": "spansIndexed",
+            }
+        )
+
+        assert response.status_code == 200, response.content
+        data = response.data["data"]
+        meta = response.data["meta"]
+        assert len(data) == 1
+        assert data[0]["transaction.method"] == "foo"
+        assert meta["dataset"] == "spansIndexed"
+
+    def test_sentry_tags_syntax(self):
+        self.store_spans(
+            [
+                self.create_span(
+                    {"sentry_tags": {"transaction.method": "foo"}}, start_ts=self.ten_mins_ago
+                ),
+            ]
+        )
+        response = self.do_request(
+            {
+                "field": ["sentry_tags[transaction.method]", "count()"],
+                "query": "",
+                "orderby": "count()",
+                "project": self.project.id,
+                "dataset": "spansIndexed",
+            }
+        )
+
+        assert response.status_code == 200, response.content
+        data = response.data["data"]
+        meta = response.data["meta"]
+        assert len(data) == 1
+        assert data[0]["sentry_tags[transaction.method]"] == "foo"
+        assert meta["dataset"] == "spansIndexed"

+ 1 - 1
tests/snuba/api/endpoints/test_organization_spans_aggregation.py

@@ -544,7 +544,7 @@ class OrganizationSpansAggregationTest(APITestCase, SnubaTestCase):
                         function="ifNull",
                         parameters=[
                             Column(
-                                name="tags[transaction.method]",
+                                name="sentry_tags[transaction.method]",
                             ),
                             "",
                         ],