Skip to content

Commit

Permalink
fix: Symbolicate error.cause on debug builds (#3920)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucas-zimerman authored Jul 5, 2024
1 parent 4cc5c27 commit 97caefa
Show file tree
Hide file tree
Showing 4 changed files with 541 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

### Fixes

- errors with cause are now correctly serialized on debug build ([#3920](https://github.com/getsentry/sentry-react-native/pull/3920))
- `sentry-expo-upload-sourcemaps` no longer requires Sentry url when uploading sourcemaps to `sentry.io` ([#3915](https://github.com/getsentry/sentry-react-native/pull/3915))
- Flavor aware Android builds use `SENTRY_AUTH_TOKEN` env as fallback when token not found in `sentry-flavor-type.properties`. ([#3917](https://github.com/getsentry/sentry-react-native/pull/3917))
- `mechanism.handled:false` should crash current session ([#3900](https://github.com/getsentry/sentry-react-native/pull/3900))
Expand Down
39 changes: 29 additions & 10 deletions src/js/integrations/debugsymbolicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { convertIntegrationFnToClass } from '@sentry/core';
import type {
Event,
EventHint,
Exception,
Integration,
IntegrationClass,
IntegrationFnResult,
StackFrame as SentryStackFrame,
} from '@sentry/types';
import { addContextToFrame, logger } from '@sentry/utils';

import type { ExtendedError } from '../utils/error';
import { getFramesToPop, isErrorLike } from '../utils/error';
import type * as ReactNative from '../vendor/react-native';
import { fetchSourceContext, getDevServer, parseErrorStack, symbolicateStackTrace } from './debugsymbolicatorutils';
Expand Down Expand Up @@ -51,13 +53,14 @@ export const DebugSymbolicator = convertIntegrationFnToClass(
) as IntegrationClass<Integration>;

async function processEvent(event: Event, hint: EventHint): Promise<Event> {
if (event.exception && isErrorLike(hint.originalException)) {
if (event.exception?.values && isErrorLike(hint.originalException)) {
// originalException is ErrorLike object
const symbolicatedFrames = await symbolicate(
hint.originalException.stack,
getFramesToPop(hint.originalException as Error),
);
symbolicatedFrames && replaceExceptionFramesInEvent(event, symbolicatedFrames);
const errorGroup = getExceptionGroup(hint.originalException);
for (const [index, error] of errorGroup.entries()) {
const symbolicatedFrames = await symbolicate(error.stack, getFramesToPop(error));

symbolicatedFrames && replaceExceptionFramesInException(event.exception.values[index], symbolicatedFrames);
}
} else if (hint.syntheticException && isErrorLike(hint.syntheticException)) {
// syntheticException is Error object
const symbolicatedFrames = await symbolicate(
Expand All @@ -66,7 +69,9 @@ async function processEvent(event: Event, hint: EventHint): Promise<Event> {
);

if (event.exception) {
symbolicatedFrames && replaceExceptionFramesInEvent(event, symbolicatedFrames);
symbolicatedFrames &&
event.exception.values &&
replaceExceptionFramesInException(event.exception.values[0], symbolicatedFrames);
} else if (event.threads) {
// RN JS doesn't have threads
symbolicatedFrames && replaceThreadFramesInEvent(event, symbolicatedFrames);
Expand Down Expand Up @@ -149,9 +154,9 @@ async function convertReactNativeFramesToSentryFrames(frames: ReactNative.StackF
* @param event Event
* @param frames StackFrame[]
*/
function replaceExceptionFramesInEvent(event: Event, frames: SentryStackFrame[]): void {
if (event.exception && event.exception.values && event.exception.values[0] && event.exception.values[0].stacktrace) {
event.exception.values[0].stacktrace.frames = frames.reverse();
function replaceExceptionFramesInException(exception: Exception, frames: SentryStackFrame[]): void {
if (exception?.stacktrace) {
exception.stacktrace.frames = frames.reverse();
}
}

Expand Down Expand Up @@ -200,3 +205,17 @@ async function addSourceContext(frame: SentryStackFrame): Promise<void> {
const lines = sourceContext.split('\n');
addContextToFrame(lines, frame);
}

/**
* Return a list containing the original exception and also the cause if found.
*
* @param originalException The original exception.
*/
function getExceptionGroup(originalException: unknown): (Error & { stack: string })[] {
const err = originalException as ExtendedError;
const errorGroup: (Error & { stack: string })[] = [];
for (let cause: ExtendedError | undefined = err; isErrorLike(cause); cause = cause.cause) {
errorGroup.push(cause);
}
return errorGroup;
}
1 change: 1 addition & 0 deletions src/js/utils/error.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface ExtendedError extends Error {
framesToPop?: number | undefined;
cause?: Error | undefined;
}

// Sentry Stack Parser is skipping lines not frames
Expand Down
Loading

0 comments on commit 97caefa

Please sign in to comment.