|
@@ -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
|
|
@@ -85,20 +85,20 @@ 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.
|
|
@@ -106,7 +106,7 @@ def _get_tags(
|
|
|
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)]
|
|
|
|
|
@@ -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:
|
|
@@ -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"
|