Skip to content

Commit 3efdb81

Browse files
committed
Update logic showing the "Reprocess Event" action
The logic was updated so that all kinds of events that have debug files are eligible for reprocessing.
1 parent f67cd5f commit 3efdb81

File tree

1 file changed

+110
-79
lines changed

1 file changed

+110
-79
lines changed

static/app/utils/displayReprocessEventAction.tsx

Lines changed: 110 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -8,106 +8,137 @@ import type {
88
} from 'sentry/types';
99
import {EntryType} from 'sentry/types';
1010

11-
const NATIVE_PLATFORMS: PlatformKey[] = ['cocoa', 'native'];
11+
/** All platforms that always use Debug Files. */
12+
const DEBUG_FILE_PLATFORMS: Set<PlatformKey> = new Set([
13+
'objc',
14+
'cocoa',
15+
'swift',
16+
'native',
17+
'c',
18+
]);
19+
/** Other platforms that may use Debug Files. */
20+
const MAYBE_DEBUG_FILE_PLATFORMS: Set<PlatformKey> = new Set(['csharp', 'java']);
21+
22+
/**
23+
* Returns whether to display the "Reprocess Event" action.
24+
*
25+
* That is the case when we have a "reprocessable" event, which is an event that needs
26+
* Debug Files for proper processing, as those Debug Files could have been uploaded *after*
27+
* the Event was ingested.
28+
*/
29+
export function displayReprocessEventAction(
30+
orgFeatures: Array<string>,
31+
event?: Event
32+
): boolean {
33+
if (!event || !orgFeatures.includes('reprocessing-v2')) {
34+
return false;
35+
}
1236

13-
// Finds all frames in a given data blob and returns it's platforms
14-
function getPlatforms(exceptionValue: ExceptionValue | StacktraceType | null) {
15-
const frames = exceptionValue?.frames ?? [];
16-
const stacktraceFrames = (exceptionValue as ExceptionValue)?.stacktrace?.frames ?? [];
37+
const eventPlatforms = getEventPlatform(event);
38+
// Check Events from platforms that always use Debug Files as a fast-path
39+
if (hasIntersection(eventPlatforms, DEBUG_FILE_PLATFORMS)) {
40+
return true;
41+
}
42+
43+
const hasDebugImages = (event?.entries ?? []).some(
44+
entry => entry.type === EntryType.DEBUGMETA && entry.data.images.length > 0
45+
);
1746

18-
if (!frames.length && !stacktraceFrames.length) {
19-
return [];
47+
// Otherwise, check alternative platforms if they actually have Debug Files
48+
if (hasIntersection(eventPlatforms, MAYBE_DEBUG_FILE_PLATFORMS) && hasDebugImages) {
49+
return true;
2050
}
2151

22-
return [...frames, ...stacktraceFrames]
23-
.map(frame => frame.platform)
24-
.filter(platform => !!platform);
25-
}
52+
// Finally, fall back to checking the `platform` of each frame
53+
const exceptionEntry = event.entries.find(
54+
entry => entry.type === EntryType.EXCEPTION
55+
) as EntryException | undefined;
2656

27-
function getStackTracePlatforms(event: Event, exceptionEntry: EntryException) {
28-
// Fetch platforms in stack traces of an exception entry
29-
const exceptionEntryPlatforms = (exceptionEntry.data.values ?? []).flatMap(
30-
getPlatforms
57+
if (!exceptionEntry) {
58+
return false;
59+
}
60+
61+
return hasIntersection(
62+
getStackTracePlatforms(event, exceptionEntry),
63+
DEBUG_FILE_PLATFORMS
3164
);
65+
}
66+
67+
/**
68+
* Returns whether the two Sets have intersecting elements.
69+
*/
70+
function hasIntersection<T>(set1: Set<T>, set2: Set<T>): boolean {
71+
for (const v of set1) {
72+
if (set2.has(v)) {
73+
return true;
74+
}
75+
}
76+
return false;
77+
}
78+
79+
/**
80+
* Returns the event platform as a Set.
81+
*/
82+
function getEventPlatform(event: Event): Set<PlatformKey> {
83+
const platforms = new Set<PlatformKey>();
84+
addPlatforms(platforms, [event]);
85+
return platforms;
86+
}
87+
88+
/**
89+
* Returns a Set of all platforms found in the `event` and `exceptionEntry`.
90+
*/
91+
function getStackTracePlatforms(
92+
event: Event,
93+
exceptionEntry: EntryException
94+
): Set<PlatformKey> {
95+
const platforms = new Set<PlatformKey>();
96+
97+
// Add platforms in stack traces of an exception entry
98+
(exceptionEntry.data.values ?? []).forEach(exc => addFramePlatforms(platforms, exc));
3299

33-
// Fetch platforms in an exception entry
100+
// Add platforms in a stack trace entry
34101
const stackTraceEntry = (event.entries.find(
35102
entry => entry.type === EntryType.STACKTRACE
36103
)?.data ?? {}) as StacktraceType;
37104

38-
// Fetch platforms in an exception entry
39-
const stackTraceEntryPlatforms = Object.keys(stackTraceEntry).flatMap(key =>
40-
getPlatforms(stackTraceEntry[key])
105+
Object.keys(stackTraceEntry).forEach(key =>
106+
addFramePlatforms(platforms, stackTraceEntry[key])
41107
);
42108

43-
// Fetch platforms in an thread entry
109+
// Add platforms in a thread entry
44110
const threadEntry = (event.entries.find(entry => entry.type === EntryType.THREADS)?.data
45111
.values ?? []) as Array<Thread>;
46112

47-
// Fetch platforms in a thread entry
48-
const threadEntryPlatforms = threadEntry.flatMap(({stacktrace}) =>
49-
getPlatforms(stacktrace)
50-
);
113+
threadEntry.forEach(({stacktrace}) => addFramePlatforms(platforms, stacktrace));
51114

52-
return new Set([
53-
...exceptionEntryPlatforms,
54-
...stackTraceEntryPlatforms,
55-
...threadEntryPlatforms,
56-
]);
115+
return platforms;
57116
}
58117

59-
// Checks whether an event indicates that it is a native event.
60-
function isNativeEvent(event: Event, exceptionEntry: EntryException) {
61-
const {platform} = event;
62-
63-
if (platform && NATIVE_PLATFORMS.includes(platform)) {
64-
return true;
65-
}
66-
67-
const stackTracePlatforms = getStackTracePlatforms(event, exceptionEntry);
68-
69-
return NATIVE_PLATFORMS.some(nativePlatform => stackTracePlatforms.has(nativePlatform));
70-
}
71-
72-
// Checks whether an event indicates that it has an associated minidump.
73-
function isMinidumpEvent(exceptionEntry: EntryException) {
74-
const {data} = exceptionEntry;
75-
return (data.values ?? []).some(value => value.mechanism?.type === 'minidump');
76-
}
118+
/**
119+
* Adds all the platforms in the frames of `exceptionValue` to the `platforms` Set.
120+
*/
121+
function addFramePlatforms(
122+
platforms: Set<PlatformKey>,
123+
exceptionValue: ExceptionValue | StacktraceType | null
124+
) {
125+
const frames = exceptionValue?.frames ?? [];
126+
const stacktraceFrames = (exceptionValue as ExceptionValue)?.stacktrace?.frames ?? [];
77127

78-
// Checks whether an event indicates that it has an apple crash report.
79-
function isAppleCrashReportEvent(exceptionEntry: EntryException) {
80-
const {data} = exceptionEntry;
81-
return (data.values ?? []).some(value => value.mechanism?.type === 'applecrashreport');
128+
addPlatforms(platforms, frames);
129+
addPlatforms(platforms, stacktraceFrames);
82130
}
83131

84-
export function displayReprocessEventAction(orgFeatures: Array<string>, event?: Event) {
85-
if (!event || !orgFeatures.includes('reprocessing-v2')) {
86-
return false;
132+
/**
133+
* Adds all the `platform` properties found in `iter` to the `platforms` Set.
134+
*/
135+
function addPlatforms(
136+
platforms: Set<PlatformKey>,
137+
iter: Array<{platform?: PlatformKey | null}>
138+
) {
139+
for (const o of iter) {
140+
if (o.platform) {
141+
platforms.add(o.platform);
142+
}
87143
}
88-
89-
const {entries} = event;
90-
const exceptionEntry = entries.find(entry => entry.type === EntryType.EXCEPTION) as
91-
| EntryException
92-
| undefined;
93-
94-
if (!exceptionEntry) {
95-
return false;
96-
}
97-
98-
// We want to show the reprocessing button if the issue in question is native or contains native frames.
99-
// The logic is taken from the symbolication pipeline in Python, where it is used to determine whether reprocessing
100-
// payloads should be stored:
101-
// https://github.com/getsentry/sentry/blob/cb7baef414890336881d67b7a8433ee47198c701/src/sentry/lang/native/processing.py#L425-L426
102-
// It is still not ideal as one can always merge native and non-native events together into one issue,
103-
// but it's the best approximation we have.
104-
if (
105-
!isMinidumpEvent(exceptionEntry) &&
106-
!isAppleCrashReportEvent(exceptionEntry) &&
107-
!isNativeEvent(event, exceptionEntry)
108-
) {
109-
return false;
110-
}
111-
112-
return true;
113144
}

0 commit comments

Comments
 (0)