5
5
6
6
import sentry_sdk
7
7
from django .http import HttpRequest , HttpResponse
8
+ from rest_framework .exceptions import ParseError
8
9
from rest_framework .request import Request
9
10
from rest_framework .response import Response
10
11
from snuba_sdk import Column , Function
27
28
from sentry .snuba .referrer import Referrer
28
29
from sentry .snuba .spans_rpc import run_trace_query
29
30
from sentry .utils .numbers import base32_encode
31
+ from sentry .utils .validators import is_event_id
30
32
31
33
# 1 worker each for spans, errors, performance issues
32
34
_query_thread_pool = ThreadPoolExecutor (max_workers = 3 )
35
+ # Mostly here for testing
36
+ ERROR_LIMIT = 10_000
33
37
34
38
35
39
class SerializedEvent (TypedDict ):
@@ -204,30 +208,39 @@ def serialize_rpc_event(
204
208
else :
205
209
return self .serialize_rpc_issue (event , group_cache )
206
210
207
- def errors_query (self , snuba_params : SnubaParams , trace_id : str ) -> DiscoverQueryBuilder :
211
+ def errors_query (
212
+ self , snuba_params : SnubaParams , trace_id : str , error_id : str | None
213
+ ) -> DiscoverQueryBuilder :
208
214
"""Run an error query, getting all the errors for a given trace id"""
209
215
# TODO: replace this with EAP calls, this query is copied from the old trace view
216
+ columns = [
217
+ "id" ,
218
+ "project.name" ,
219
+ "project.id" ,
220
+ "timestamp" ,
221
+ "timestamp_ms" ,
222
+ "trace.span" ,
223
+ "transaction" ,
224
+ "issue" ,
225
+ "title" ,
226
+ "message" ,
227
+ "tags[level]" ,
228
+ ]
229
+ orderby = ["id" ]
230
+ # If there's an error_id included in the request, bias the orderby to try to return that error_id over others so
231
+ # that we can render it in the trace view, even if we hit the error_limit
232
+ if error_id is not None :
233
+ columns .append (f'to_other(id, "{ error_id } ", 0, 1) AS target' )
234
+ orderby .insert (0 , "-target" )
210
235
return DiscoverQueryBuilder (
211
236
Dataset .Events ,
212
237
params = {},
213
238
snuba_params = snuba_params ,
214
239
query = f"trace:{ trace_id } " ,
215
- selected_columns = [
216
- "id" ,
217
- "project.name" ,
218
- "project.id" ,
219
- "timestamp" ,
220
- "timestamp_ms" ,
221
- "trace.span" ,
222
- "transaction" ,
223
- "issue" ,
224
- "title" ,
225
- "message" ,
226
- "tags[level]" ,
227
- ],
240
+ selected_columns = columns ,
228
241
# Don't add timestamp to this orderby as snuba will have to split the time range up and make multiple queries
229
- orderby = [ "id" ] ,
230
- limit = 10_000 ,
242
+ orderby = orderby ,
243
+ limit = ERROR_LIMIT ,
231
244
config = QueryBuilderConfig (
232
245
auto_fields = True ,
233
246
),
@@ -292,7 +305,9 @@ def run_perf_issues_query(self, occurrence_query: DiscoverQueryBuilder):
292
305
return result
293
306
294
307
@sentry_sdk .tracing .trace
295
- def query_trace_data (self , snuba_params : SnubaParams , trace_id : str ) -> list [SerializedEvent ]:
308
+ def query_trace_data (
309
+ self , snuba_params : SnubaParams , trace_id : str , error_id : str | None = None
310
+ ) -> list [SerializedEvent ]:
296
311
"""Queries span/error data for a given trace"""
297
312
# This is a hack, long term EAP will store both errors and performance_issues eventually but is not ready
298
313
# currently. But we want to move performance data off the old tables immediately. To keep the code simpler I'm
@@ -302,7 +317,7 @@ def query_trace_data(self, snuba_params: SnubaParams, trace_id: str) -> list[Ser
302
317
# the thread pool, database connections can hang around as the threads are not cleaned
303
318
# up. Because of that, tests can fail during tear down as there are active connections
304
319
# to the database preventing a DROP.
305
- errors_query = self .errors_query (snuba_params , trace_id )
320
+ errors_query = self .errors_query (snuba_params , trace_id , error_id )
306
321
occurrence_query = self .perf_issues_query (snuba_params , trace_id )
307
322
308
323
spans_future = _query_thread_pool .submit (
@@ -380,10 +395,14 @@ def get(self, request: Request, organization: Organization, trace_id: str) -> Ht
380
395
381
396
update_snuba_params_with_timestamp (request , snuba_params )
382
397
398
+ error_id = request .GET .get ("errorId" )
399
+ if error_id is not None and not is_event_id (error_id ):
400
+ raise ParseError (f"eventId: { error_id } needs to be a valid uuid" )
401
+
383
402
def data_fn (offset : int , limit : int ) -> list [SerializedEvent ]:
384
403
"""offset and limit don't mean anything on this endpoint currently"""
385
404
with handle_query_errors ():
386
- spans = self .query_trace_data (snuba_params , trace_id )
405
+ spans = self .query_trace_data (snuba_params , trace_id , error_id )
387
406
return spans
388
407
389
408
return self .paginate (
0 commit comments