Skip to content

Commit

Permalink
feat(metrics): extend counter condtion (#73427)
Browse files Browse the repository at this point in the history
  • Loading branch information
obostjancic committed Jun 28, 2024
1 parent ac278be commit 54c9857
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 14 deletions.
58 changes: 47 additions & 11 deletions src/sentry/snuba/metrics/span_attribute_extraction.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from collections.abc import Sequence
from typing import Any, Literal, NotRequired, TypedDict
from typing import Literal, NotRequired, TypedDict

from sentry.api import event_search
from sentry.api.event_search import ParenExpression, QueryToken, SearchFilter
Expand Down Expand Up @@ -85,28 +85,28 @@ def convert_to_metric_spec(extraction_rule: MetricsExtractionRule) -> SpanAttrib
"category": "span",
"mri": extraction_rule.generate_mri(),
"field": field,
"tags": _get_tags(extraction_rule.tags, parsed_conditions),
"condition": _get_rule_condition(parsed_conditions),
"tags": _get_tags(extraction_rule, parsed_conditions),
"condition": _get_rule_condition(extraction_rule, parsed_conditions),
}


def _get_field(extraction_rule: MetricsExtractionRule) -> str | None:
if extraction_rule.type == "c":
if _is_counter(extraction_rule):
return None

return _map_span_attribute_name(extraction_rule.span_attribute)


def _get_tags(
explicitly_defined_tags: set[str], conditions: Sequence[QueryToken] | None
extraction_rule: MetricsExtractionRule, conditions: Sequence[QueryToken] | None
) -> list[TagSpec]:
"""
Merges the explicitly defined tags with the tags extracted from the search conditions.
"""
token_list = _flatten_query_tokens(conditions) if conditions else []
search_token_keys = {token.key.name for token in token_list}

tag_keys = explicitly_defined_tags.union(search_token_keys)
tag_keys = extraction_rule.tags.union(search_token_keys)

return [TagSpec(key=key, field=_map_span_attribute_name(key)) for key in sorted(tag_keys)]

Expand All @@ -133,14 +133,46 @@ def _parse_conditions(conditions: Sequence[str] | None) -> Sequence[QueryToken]:
return event_search.parse_search_query(search_query)


def _get_rule_condition(parsed_search_query: Sequence[Any] | None) -> RuleCondition | None:
if not parsed_search_query:
return None
def _get_rule_condition(
extraction_rule: MetricsExtractionRule, parsed_conditions: Sequence[QueryToken]
) -> RuleCondition | None:
if not parsed_conditions:
if not _is_counter(extraction_rule):
return None

return _get_exists_condition(extraction_rule.span_attribute)

return SearchQueryConverter(
parsed_search_query, field_mapper=_map_span_attribute_name
condition_dict = SearchQueryConverter(
parsed_conditions, field_mapper=_map_span_attribute_name
).convert()

return (
_append_exists_condition(condition_dict, extraction_rule.span_attribute)
if _is_counter(extraction_rule)
else condition_dict
)


def _append_exists_condition(rule_condition: RuleCondition, span_attribute: str) -> RuleCondition:
return {
"op": "and",
"inner": [
rule_condition,
_get_exists_condition(span_attribute),
],
}


def _get_exists_condition(span_attribute: str) -> RuleCondition:
return {
"op": "not",
"inner": {
"name": _map_span_attribute_name(span_attribute),
"op": "eq",
"value": None,
},
}


def _map_span_attribute_name(span_attribute: str) -> str:
if span_attribute in _TOP_LEVEL_SPAN_ATTRIBUTES:
Expand All @@ -152,3 +184,7 @@ def _map_span_attribute_name(span_attribute: str) -> str:
sanitized_span_attr = span_attribute.replace(".", "\\.")

return f"span.data.{sanitized_span_attr}"


def _is_counter(extraction_rule: MetricsExtractionRule) -> bool:
return extraction_rule.type == "c"
5 changes: 4 additions & 1 deletion tests/sentry/relay/config/test_metric_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2168,7 +2168,10 @@ def test_get_span_attribute_metrics(default_project: Project) -> None:
},
{
"category": "span",
"condition": None,
"condition": {
"op": "not",
"inner": {"name": "span.duration", "op": "eq", "value": None},
},
"field": None,
"mri": "c:custom/span.duration@none",
"tags": [],
Expand Down
25 changes: 23 additions & 2 deletions tests/sentry/snuba/metrics/test_span_attribute_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,33 @@ def test_counter():

assert not metric_spec["field"]
assert metric_spec["mri"] == "c:custom/foobar@none"
assert metric_spec["tags"] == []
assert metric_spec["condition"] == {
"inner": {"name": "span.data.foobar", "op": "eq", "value": None},
"op": "not",
}


def test_counter_extends_conditions():
rule = MetricsExtractionRule(
span_attribute="foobar", type="c", unit="none", tags=set(), conditions=["abc:xyz"]
)

metric_spec = convert_to_metric_spec(rule)

assert not metric_spec["field"]
assert metric_spec["mri"] == "c:custom/foobar@none"
assert metric_spec["condition"] == {
"op": "and",
"inner": [
{"op": "eq", "name": "span.data.abc", "value": "xyz"},
{"inner": {"name": "span.data.foobar", "op": "eq", "value": None}, "op": "not"},
],
}


def test_empty_conditions():
rule = MetricsExtractionRule(
span_attribute="foobar", type="c", unit="none", tags=set(), conditions=[""]
span_attribute="foobar", type="d", unit="none", tags=set(), conditions=[""]
)

metric_spec = convert_to_metric_spec(rule)
Expand Down

0 comments on commit 54c9857

Please sign in to comment.