66from datetime import datetime , timedelta
77from typing import Any , Literal , NotRequired , TypedDict , cast
88
9+ import sentry_sdk
910from rest_framework import serializers
1011from rest_framework .exceptions import ParseError
1112from rest_framework .request import Request
@@ -66,6 +67,8 @@ class OrganizationTracesSerializer(serializers.Serializer):
6667 )
6768 field = serializers .ListField (required = True , allow_empty = False , child = serializers .CharField ())
6869 sort = serializers .ListField (required = False , allow_empty = True , child = serializers .CharField ())
70+ metricsMax = serializers .FloatField (required = False )
71+ metricsMin = serializers .FloatField (required = False )
6972 metricsQuery = serializers .CharField (required = False )
7073 mri = serializers .CharField (required = False )
7174 query = serializers .ListField (
@@ -107,6 +110,8 @@ def get(self, request: Request, organization: Organization) -> Response:
107110 # Filter out empty queries as they do not do anything to change the results.
108111 user_queries = [query .strip () for query in serialized .get ("query" , []) if query .strip ()],
109112 suggested_query = serialized .get ("suggestedQuery" , "" ),
113+ metrics_max = serialized .get ("metricsMax" ),
114+ metrics_min = serialized .get ("metricsMin" ),
110115 metrics_query = serialized .get ("metricsQuery" , "" ),
111116 mri = serialized .get ("mri" ),
112117 sort = serialized .get ("sort" ),
@@ -148,6 +153,8 @@ def __init__(
148153 fields : list [str ],
149154 user_queries : list [str ],
150155 suggested_query : str ,
156+ metrics_max : float | None ,
157+ metrics_min : float | None ,
151158 metrics_query : str ,
152159 mri : str | None ,
153160 sort : str | None ,
@@ -163,6 +170,8 @@ def __init__(
163170 self .fields = fields
164171 self .user_queries = user_queries
165172 self .suggested_query = suggested_query
173+ self .metrics_max = metrics_max
174+ self .metrics_min = metrics_min
166175 self .metrics_query = metrics_query
167176 self .mri = mri
168177 self .sort = sort
@@ -326,6 +335,8 @@ def get_traces_matching_metric_conditions(
326335 params = params ,
327336 snuba_params = snuba_params ,
328337 fields = ["trace" ],
338+ max = self .metrics_max ,
339+ min = self .metrics_min ,
329340 query = self .metrics_query ,
330341 referrer = Referrer .API_TRACE_EXPLORER_METRICS_SPANS_LIST ,
331342 )
@@ -674,6 +685,23 @@ def process_final_results(
674685 for row in suggested_spans_results ["data" ]:
675686 traces_suggested_spans [row ["trace" ]].append (row )
676687
688+ for row in traces_metas_results ["data" ]:
689+ if not traces_user_spans [row ["trace" ]]:
690+ context = {
691+ "trace" : row ["trace" ],
692+ "start" : row ["first_seen()" ],
693+ "end" : row ["last_seen()" ],
694+ "params" : self .params ,
695+ "user_query" : self .user_queries ,
696+ "mri" : self .mri ,
697+ "metrics_query" : self .metrics_query ,
698+ "metrics_min" : self .metrics_min ,
699+ "metrics_max" : self .metrics_max ,
700+ }
701+ sentry_sdk .capture_message (
702+ "trace missing spans" , contexts = {"trace_missing_spans" : context }
703+ )
704+
677705 return [
678706 {
679707 "trace" : row ["trace" ],
@@ -696,6 +724,7 @@ def process_final_results(
696724 ],
697725 }
698726 for row in traces_metas_results ["data" ]
727+ if traces_user_spans [row ["trace" ]]
699728 ]
700729
701730 def process_meta_results (self , results ):
@@ -1120,6 +1149,7 @@ def stack_clear(trace, until=None):
11201149 precise_end ,
11211150 traces_range [trace ],
11221151 )
1152+
11231153 row ["precise.start_ts" ] = precise_start
11241154 row ["precise.finish_ts" ] = precise_end
11251155 row ["quantized.start_ts" ] = quantized_start
0 commit comments