Skip to content

Commit 825fe89

Browse files
authored
fix(browser): Mark stack trace from captureMessage with attachStacktrace: true as synthetic (#14668)
Add the `synthetic: true` flag to the `mechanism` of a synthetic exception captured alongside a `captureMessage` call. Setting this property marks the exception as synthetic for processing on the backend
1 parent 4871906 commit 825fe89

File tree

5 files changed

+69
-1
lines changed

5 files changed

+69
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
attachStacktrace: true,
8+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sentry.captureMessage('foo');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/core';
3+
4+
import { sentryTest } from '../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers';
6+
7+
sentryTest(
8+
'captures a simple message string with stack trace if `attachStackTrace` is `true`',
9+
async ({ getLocalTestUrl, page }) => {
10+
const url = await getLocalTestUrl({ testDir: __dirname });
11+
12+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
13+
14+
expect(eventData.message).toBe('foo');
15+
expect(eventData.level).toBe('info');
16+
expect(eventData.exception?.values?.[0]).toEqual({
17+
mechanism: {
18+
handled: true,
19+
type: 'generic',
20+
synthetic: true,
21+
},
22+
stacktrace: {
23+
frames: expect.arrayContaining([expect.any(Object), expect.any(Object)]),
24+
},
25+
value: 'foo',
26+
});
27+
},
28+
);

packages/browser/src/eventbuilder.ts

+1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ function eventFromString(
347347
values: [{ value: message, stacktrace: { frames } }],
348348
};
349349
}
350+
addExceptionMechanism(event, { synthetic: true });
350351
}
351352

352353
if (isParameterizedString(message)) {

packages/browser/test/eventbuilder.test.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { afterEach, describe, expect, it, vi } from 'vitest';
66

77
import { defaultStackParser } from '../src';
8-
import { eventFromUnknownInput, extractMessage, extractType } from '../src/eventbuilder';
8+
import { eventFromMessage, eventFromUnknownInput, extractMessage, extractType } from '../src/eventbuilder';
99

1010
vi.mock('@sentry/core', async requireActual => {
1111
return {
@@ -231,3 +231,33 @@ describe('extractName', () => {
231231
expect(name).toBeUndefined();
232232
});
233233
});
234+
235+
describe('eventFromMessage ', () => {
236+
it('creates an event from a string message', async () => {
237+
const event = await eventFromMessage(defaultStackParser, 'Test message');
238+
expect(event).toEqual({
239+
level: 'info',
240+
message: 'Test message',
241+
});
242+
});
243+
244+
it('creates an event with a synthetic stack trace if attachStacktrace is true', async () => {
245+
const syntheticException = new Error('Test message');
246+
const event = await eventFromMessage(defaultStackParser, 'Test message', 'info', { syntheticException }, true);
247+
expect(event.exception?.values?.[0]).toEqual(
248+
expect.objectContaining({
249+
mechanism: { handled: true, synthetic: true, type: 'generic' },
250+
stacktrace: {
251+
frames: expect.arrayContaining([expect.any(Object), expect.any(Object)]),
252+
},
253+
value: 'Test message',
254+
}),
255+
);
256+
});
257+
258+
it("doesn't add a synthetic stack trace if attachStacktrace is false, even if one is passed-", async () => {
259+
const syntheticException = new Error('Test message');
260+
const event = await eventFromMessage(defaultStackParser, 'Test message', 'info', { syntheticException }, false);
261+
expect(event.exception).toBeUndefined();
262+
});
263+
});

0 commit comments

Comments
 (0)