Skip to content

Commit 3f220c9

Browse files
committed
Fixes
1 parent 44784f7 commit 3f220c9

File tree

4 files changed

+268
-149
lines changed

4 files changed

+268
-149
lines changed

src/sentry/api/endpoints/seer_rpc.py

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from sentry.hybridcloud.rpc.service import RpcAuthenticationSetupException, RpcResolutionException
2828
from sentry.hybridcloud.rpc.sig import SerializableFunctionValueException
2929
from sentry.models.organization import Organization
30+
from sentry.seer.autofix_tools import get_error_event_details, get_profile_details
3031
from sentry.seer.fetch_issues.fetch_issues_given_patches import get_issues_related_to_file_patches
3132
from sentry.silo.base import SiloMode
3233
from sentry.utils.env import in_test_environment
@@ -169,6 +170,8 @@ def get_organization_autofix_consent(*, org_id: int) -> dict:
169170
"get_organization_slug": get_organization_slug,
170171
"get_organization_autofix_consent": get_organization_autofix_consent,
171172
"get_issues_related_to_file_patches": get_issues_related_to_file_patches,
173+
"get_error_event_details": get_error_event_details,
174+
"get_profile_details": get_profile_details,
172175
}
173176

174177

src/sentry/seer/autofix.py

+29-63
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,15 @@ def _get_trace_tree_for_event(event: Event | GroupEvent, project: Project) -> di
179179

180180
event_node = {
181181
"event_id": event.event_id,
182-
"datetime": event.datetime,
182+
"datetime": event_data.get("start_timestamp", float("inf")),
183183
"span_id": event_data.get("contexts", {}).get("trace", {}).get("span_id"),
184184
"parent_span_id": event_data.get("contexts", {}).get("trace", {}).get("parent_span_id"),
185185
"is_transaction": is_transaction,
186186
"is_error": is_error,
187187
"is_current_project": event.project_id == project.id,
188188
"project_slug": event.project.slug,
189189
"project_id": event.project_id,
190+
"platform": event.platform,
190191
"children": [],
191192
}
192193

@@ -223,7 +224,6 @@ def _get_trace_tree_for_event(event: Event | GroupEvent, project: Project) -> di
223224
event_node.update(
224225
{
225226
"title": f"{op} - {transaction_title}" if op else transaction_title,
226-
"platform": event.platform,
227227
"duration": duration_str,
228228
"profile_id": profile_id,
229229
"span_ids": span_ids, # Store for later use
@@ -249,7 +249,6 @@ def _get_trace_tree_for_event(event: Event | GroupEvent, project: Project) -> di
249249
event_node.update(
250250
{
251251
"title": error_title,
252-
"platform": event.platform,
253252
}
254253
)
255254

@@ -346,91 +345,58 @@ def sort_tree(node):
346345
# Sort children at each level
347346
sorted_tree = [sort_tree(root) for root in root_events]
348347

349-
# Clean up temporary fields before returning
350-
def cleanup_node(node):
351-
if "span_ids" in node:
352-
del node["span_ids"]
353-
for child in node["children"]:
354-
cleanup_node(child)
355-
return node
356-
357-
cleaned_tree = [cleanup_node(root) for root in sorted_tree]
358-
359348
return {
360349
"trace_id": event.trace_id,
361350
"org_id": project.organization_id,
362-
"events": cleaned_tree,
351+
"events": sorted_tree,
363352
}
364353

365354

366355
def _get_profile_from_trace_tree(
367356
trace_tree: dict[str, Any] | None, event: Event | GroupEvent | None, project: Project
368357
) -> dict[str, Any] | None:
369358
"""
370-
Finds the profile for the transaction that is a parent of our error event.
359+
Finds the profile for the transaction that contains our error event.
371360
"""
372361
if not trace_tree or not event:
373362
return None
374363

375364
events = trace_tree.get("events", [])
376-
event_id = event.event_id
377-
378-
# First, find our error event in the tree and track parent transactions
379-
# 1. Find the error event node and also build a map of parent-child relationships
380-
# 2. Walk up from the error event to find a transaction with a profile
381-
382-
child_to_parent = {}
383-
384-
def build_parent_map(node, parent=None):
385-
node_id = node.get("event_id")
386-
387-
if parent:
388-
child_to_parent[node_id] = parent
389-
390-
for child in node.get("children", []):
391-
build_parent_map(child, node)
365+
event_span_id = event.data.get("contexts", {}).get("trace", {}).get("span_id")
392366

393-
# Build the parent-child map for the entire tree
394-
for root_node in events:
395-
build_parent_map(root_node)
367+
if not event_span_id:
368+
return None
396369

397-
# Find our error node in the flattened tree
398-
error_node = None
399-
all_nodes = []
370+
# Flatten all events in the tree for easier traversal
371+
all_events = []
400372

401-
def collect_all_nodes(node):
402-
all_nodes.append(node)
373+
def collect_all_events(node):
374+
all_events.append(node)
403375
for child in node.get("children", []):
404-
collect_all_nodes(child)
376+
collect_all_events(child)
405377

406378
for root_node in events:
407-
collect_all_nodes(root_node)
379+
collect_all_events(root_node)
380+
381+
# Find the first transaction that contains the event's span ID
382+
# or has a span_id matching the event's span_id
383+
matching_transaction = None
384+
for node in all_events:
385+
if node.get("is_transaction", False):
386+
# Check if this transaction's span_id matches the event_span_id
387+
if node.get("span_id") == event_span_id:
388+
matching_transaction = node
389+
break
408390

409-
for node in all_nodes:
410-
if node.get("event_id") == event_id:
411-
error_node = node
412-
break
391+
# Check if this transaction contains the event_span_id in its span_ids
392+
if event_span_id in node.get("span_ids", []):
393+
matching_transaction = node
394+
break
413395

414-
if not error_node:
396+
if not matching_transaction or not matching_transaction.get("profile_id"):
415397
return None
416398

417-
# Now walk up the tree to find a transaction with a profile
418-
profile_id = None
419-
current_node = error_node
420-
while current_node:
421-
if current_node.get("profile_id"):
422-
profile_id = current_node.get("profile_id")
423-
break
424-
425-
# Move up to parent - child_to_parent maps child event IDs to parent node objects
426-
parent_node = child_to_parent.get(current_node.get("event_id"))
427-
if not parent_node:
428-
# Reached the root without finding a suitable transaction
429-
return None
430-
current_node = parent_node
431-
432-
if not profile_id:
433-
return None
399+
profile_id = matching_transaction.get("profile_id")
434400

435401
# Fetch the profile data
436402
response = get_from_profiling_service(

src/sentry/seer/autofix_tools.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import orjson
2+
3+
from sentry import eventstore
4+
from sentry.api.serializers import EventSerializer, serialize
5+
from sentry.profiles.utils import get_from_profiling_service
6+
from sentry.seer.autofix import _convert_profile_to_execution_tree
7+
8+
9+
def get_profile_details(organization_id: str, project_id: int, profile_id: str):
10+
response = get_from_profiling_service(
11+
"GET",
12+
f"/organizations/{organization_id}/projects/{project_id}/profiles/{profile_id}",
13+
params={"format": "sample"},
14+
)
15+
16+
if response.status == 200:
17+
profile = orjson.loads(response.data)
18+
execution_tree = _convert_profile_to_execution_tree(profile)
19+
output = (
20+
None
21+
if not execution_tree
22+
else {
23+
"profile_matches_issue": True,
24+
"execution_tree": execution_tree,
25+
}
26+
)
27+
return output
28+
29+
30+
def get_error_event_details(project_id: int, event_id: str):
31+
event = eventstore.backend.get_event_by_id(project_id, event_id)
32+
if not event:
33+
return None
34+
35+
serialized_event = serialize(objects=event, user=None, serializer=EventSerializer())
36+
return serialized_event

0 commit comments

Comments
 (0)