Skip to content

Commit

Permalink
fix(client): Keep captureMessage stack trace in exception (#3988)
Browse files Browse the repository at this point in the history
Co-authored-by: LucasZF <lucas-zimerman1@hotmail.com>
  • Loading branch information
krystofwoldrich and lucas-zimerman committed Aug 6, 2024
1 parent 35e3281 commit f46f26a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 21 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Fixes

- `Sentry.captureMessage` stack trace is in `event.exception` (moved from `event.threads`) ([#3635](https://github.com/getsentry/sentry-react-native/pull/3635), [#3988](https://github.com/getsentry/sentry-react-native/pull/3988))
- To revert to the old behavior (causing the stack to be unsymbolicated) use `useThreadsForMessageStack` option

## 5.27.0

### Fixes
Expand Down
34 changes: 19 additions & 15 deletions src/js/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,26 @@ export class ReactNativeClient extends BaseClient<ReactNativeClientOptions> {
* @inheritDoc
*/
public eventFromMessage(message: string, level?: SeverityLevel, hint?: EventHint): PromiseLike<Event> {
return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace).then(
(event: Event) => {
// TMP! Remove this function once JS SDK uses threads for messages
if (!event.exception?.values || event.exception.values.length <= 0) {
if (this._options.useThreadsForMessageStack) {
return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace).then(
(event: Event) => {
// TMP! Remove this function once JS SDK uses threads for messages
if (!event.exception?.values || event.exception.values.length <= 0) {
return event;
}
const values = event.exception.values.map(
(exception: Exception): Thread => ({
stacktrace: exception.stacktrace,
}),
);
(event as { threads?: { values: Thread[] } }).threads = { values };
delete event.exception;
return event;
}
const values = event.exception.values.map(
(exception: Exception): Thread => ({
stacktrace: exception.stacktrace,
}),
);
(event as { threads?: { values: Thread[] } }).threads = { values };
delete event.exception;
return event;
},
);
},
);
}

return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/js/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ export interface BaseReactNativeOptions {
*/
replaysOnErrorSampleRate?: number;
};

/**
* This options changes the placement of the attached stacktrace of `captureMessage` in the event.
*
* @default false
* @deprecated This option will be removed in the next major version. Use `beforeSend` instead.
*/
useThreadsForMessageStack?: boolean;
}

export interface ReactNativeTransportOptions extends BrowserTransportOptions {
Expand Down
33 changes: 27 additions & 6 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,16 @@ describe('Tests ReactNativeClient', () => {

beforeEach(() => {
mockTransportSend = jest.fn(() => Promise.resolve());
});

afterEach(() => {
mockTransportSend.mockClear();
});

const getMessageEventFrom = (func: jest.Mock) =>
func.mock.calls[0][firstArg][envelopeItems][0][envelopeItemPayload];

test('captureMessage contains stack trace in exception', async () => {
client = new ReactNativeClient({
...DEFAULT_OPTIONS,
attachStacktrace: true,
Expand All @@ -359,16 +369,27 @@ describe('Tests ReactNativeClient', () => {
flush: jest.fn(),
}),
} as ReactNativeClientOptions);
});

afterEach(() => {
mockTransportSend.mockClear();
const mockSyntheticExceptionFromHub = new Error();
client.captureMessage('test message', 'error', { syntheticException: mockSyntheticExceptionFromHub });
expect(getMessageEventFrom(mockTransportSend).exception.values.length).toBeGreaterThan(0);
expect(getMessageEventFrom(mockTransportSend).exception).toBeDefined();
expect(getMessageEventFrom(mockTransportSend).threads).toBeUndefined();
});

const getMessageEventFrom = (func: jest.Mock) =>
func.mock.calls[0][firstArg][envelopeItems][0][envelopeItemPayload];
test('captureMessage contains stack trace in exception', async () => {
client = new ReactNativeClient({
...DEFAULT_OPTIONS,
attachStacktrace: true,
stackParser: defaultStackParser,
dsn: EXAMPLE_DSN,
transport: () => ({
send: mockTransportSend,
flush: jest.fn(),
}),
useThreadsForMessageStack: true,
} as ReactNativeClientOptions);

test('captureMessage contains stack trace in threads', async () => {
const mockSyntheticExceptionFromHub = new Error();
client.captureMessage('test message', 'error', { syntheticException: mockSyntheticExceptionFromHub });
expect(getMessageEventFrom(mockTransportSend).threads.values.length).toBeGreaterThan(0);
Expand Down

0 comments on commit f46f26a

Please sign in to comment.