Skip to content

Commit 1c3f37b

Browse files
authored
ref(core)!: Mark exceptions from captureConsoleIntegration as handled: true by default (#14734)
Flip the default value for the `handled` flag in exceptions captured by `captureConsoleIntegration`. This integration only captures exceptions if `attachStackTrace` is set to `true`. Added another integration test as a follow-up as promised in #14664.
1 parent 4873c6d commit 1c3f37b

File tree

7 files changed

+213
-12
lines changed

7 files changed

+213
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/browser';
2+
import { captureConsoleIntegration } from '@sentry/browser';
3+
4+
window.Sentry = Sentry;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [captureConsoleIntegration()],
9+
autoSessionTracking: false,
10+
attachStacktrace: true,
11+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
console.log('console log');
2+
console.warn('console warn');
3+
console.error('console error');
4+
console.info('console info');
5+
console.trace('console trace');
6+
7+
console.error(new Error('console error with error object'));
8+
console.trace(new Error('console trace with error object'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/core';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { getMultipleSentryEnvelopeRequests } from '../../../utils/helpers';
6+
7+
sentryTest(
8+
'captures console messages correctly and adds a synthetic stack trace if `attachStackTrace` is set to `true`',
9+
async ({ getLocalTestUrl, page }) => {
10+
const url = await getLocalTestUrl({ testDir: __dirname });
11+
12+
const [, events] = await Promise.all([page.goto(url), getMultipleSentryEnvelopeRequests<Event>(page, 7)]);
13+
14+
expect(events).toHaveLength(7);
15+
16+
const logEvent = events.find(event => event.message === 'console log');
17+
const warnEvent = events.find(event => event.message === 'console warn');
18+
const infoEvent = events.find(event => event.message === 'console info');
19+
const errorEvent = events.find(event => event.message === 'console error');
20+
const traceEvent = events.find(event => event.message === 'console trace');
21+
const errorWithErrorEvent = events.find(
22+
event => event.exception && event.exception.values?.[0].value === 'console error with error object',
23+
);
24+
const traceWithErrorEvent = events.find(
25+
event => event.exception && event.exception.values?.[0].value === 'console trace with error object',
26+
);
27+
28+
expect(logEvent).toEqual(
29+
expect.objectContaining({
30+
level: 'log',
31+
logger: 'console',
32+
extra: {
33+
arguments: ['console log'],
34+
},
35+
message: 'console log',
36+
}),
37+
);
38+
expect(logEvent?.exception?.values![0]).toMatchObject({
39+
mechanism: {
40+
handled: true,
41+
type: 'console',
42+
synthetic: true,
43+
},
44+
value: 'console log',
45+
stacktrace: {
46+
frames: expect.any(Array),
47+
},
48+
});
49+
50+
expect(warnEvent).toEqual(
51+
expect.objectContaining({
52+
level: 'warning',
53+
logger: 'console',
54+
extra: {
55+
arguments: ['console warn'],
56+
},
57+
message: 'console warn',
58+
}),
59+
);
60+
expect(warnEvent?.exception?.values![0]).toMatchObject({
61+
mechanism: {
62+
handled: true,
63+
type: 'console',
64+
synthetic: true,
65+
},
66+
value: 'console warn',
67+
stacktrace: {
68+
frames: expect.any(Array),
69+
},
70+
});
71+
72+
expect(infoEvent).toEqual(
73+
expect.objectContaining({
74+
level: 'info',
75+
logger: 'console',
76+
extra: {
77+
arguments: ['console info'],
78+
},
79+
message: 'console info',
80+
}),
81+
);
82+
expect(infoEvent?.exception?.values![0]).toMatchObject({
83+
mechanism: {
84+
handled: true,
85+
type: 'console',
86+
synthetic: true,
87+
},
88+
value: 'console info',
89+
stacktrace: {
90+
frames: expect.any(Array),
91+
},
92+
});
93+
94+
expect(errorEvent).toEqual(
95+
expect.objectContaining({
96+
level: 'error',
97+
logger: 'console',
98+
extra: {
99+
arguments: ['console error'],
100+
},
101+
message: 'console error',
102+
}),
103+
);
104+
expect(errorEvent?.exception?.values![0]).toMatchObject({
105+
mechanism: {
106+
handled: true,
107+
type: 'console',
108+
synthetic: true,
109+
},
110+
value: 'console error',
111+
stacktrace: {
112+
frames: expect.any(Array),
113+
},
114+
});
115+
116+
expect(traceEvent).toEqual(
117+
expect.objectContaining({
118+
level: 'log',
119+
logger: 'console',
120+
extra: {
121+
arguments: ['console trace'],
122+
},
123+
message: 'console trace',
124+
}),
125+
);
126+
expect(traceEvent?.exception?.values![0]).toMatchObject({
127+
mechanism: {
128+
handled: true,
129+
type: 'console',
130+
synthetic: true,
131+
},
132+
value: 'console trace',
133+
stacktrace: {
134+
frames: expect.any(Array),
135+
},
136+
});
137+
138+
expect(errorWithErrorEvent).toEqual(
139+
expect.objectContaining({
140+
level: 'error',
141+
logger: 'console',
142+
extra: {
143+
arguments: [
144+
{
145+
message: 'console error with error object',
146+
name: 'Error',
147+
stack: expect.any(String),
148+
},
149+
],
150+
},
151+
exception: expect.any(Object),
152+
}),
153+
);
154+
expect(errorWithErrorEvent?.exception?.values?.[0].value).toBe('console error with error object');
155+
expect(errorWithErrorEvent?.exception?.values?.[0].mechanism).toEqual({
156+
handled: true,
157+
type: 'console',
158+
});
159+
expect(traceWithErrorEvent).toEqual(
160+
expect.objectContaining({
161+
level: 'log',
162+
logger: 'console',
163+
extra: {
164+
arguments: [
165+
{
166+
message: 'console trace with error object',
167+
name: 'Error',
168+
stack: expect.any(String),
169+
},
170+
],
171+
},
172+
}),
173+
);
174+
expect(traceWithErrorEvent?.exception?.values?.[0].value).toBe('console trace with error object');
175+
},
176+
);

dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p
9696
);
9797
expect(errorWithErrorEvent?.exception?.values?.[0].value).toBe('console error with error object');
9898
expect(errorWithErrorEvent?.exception?.values?.[0].mechanism).toEqual({
99-
// TODO (v9): Adjust to true after changing the integration's default value
100-
handled: false,
99+
handled: true,
101100
type: 'console',
102101
});
103102
expect(traceWithErrorEvent).toEqual(

docs/migration/v8-to-v9.md

+11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ If you need to support older browsers, we recommend transpiling your code using
7878

7979
## 2. Behavior Changes
8080

81+
### `@sentry/core` / All SDKs
82+
83+
- If you use the optional `captureConsoleIntegration` and set `attachStackTrace: true` in your `Sentry.init` call, console messages will no longer be marked as unhandled (i.e. `handled: false`) but as handled (i.e. `handled: true`). If you want to keep sending them as unhandled, configure the `handled` option when adding the integration:
84+
85+
```js
86+
Sentry.init({
87+
integrations: [Sentry.captureConsoleIntegration({ handled: false })],
88+
attachStackTrace: true,
89+
});
90+
```
91+
8192
### `@sentry/node`
8293

8394
- When `skipOpenTelemetrySetup: true` is configured, `httpIntegration({ spans: false })` will be configured by default. This means that you no longer have to specify this yourself in this scenario. With this change, no spans are emitted once `skipOpenTelemetrySetup: true` is configured, without any further configuration being needed.

packages/core/src/integrations/captureconsole.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@ import { GLOBAL_OBJ } from '../utils-hoist/worldwide';
1212
interface CaptureConsoleOptions {
1313
levels?: string[];
1414

15-
// TODO(v9): Flip default value to `true` and adjust JSDoc!
1615
/**
17-
* By default, Sentry will mark captured console messages as unhandled.
18-
* Set this to `true` if you want to mark them as handled instead.
16+
* By default, Sentry will mark captured console messages as handled.
17+
* Set this to `false` if you want to mark them as unhandled instead.
1918
*
20-
* Note: in v9 of the SDK, this option will default to `true`, meaning the default behavior will change to mark console messages as handled.
21-
* @default false
19+
* @default true
2220
*/
2321
handled?: boolean;
2422
}
@@ -27,8 +25,7 @@ const INTEGRATION_NAME = 'CaptureConsole';
2725

2826
const _captureConsoleIntegration = ((options: CaptureConsoleOptions = {}) => {
2927
const levels = options.levels || CONSOLE_LEVELS;
30-
// TODO(v9): Flip default value to `true`
31-
const handled = !!options.handled;
28+
const handled = options.handled ?? true;
3229

3330
return {
3431
name: INTEGRATION_NAME,

packages/core/test/lib/integrations/captureconsole.test.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,7 @@ describe('CaptureConsole setup', () => {
306306
});
307307

308308
describe('exception mechanism', () => {
309-
// TODO (v9): Flip this below after adjusting the default value for `handled` in the integration
310-
it("marks captured exception's mechanism as unhandled by default", () => {
309+
it("marks captured exception's mechanism as handled by default", () => {
311310
const captureConsole = captureConsoleIntegration({ levels: ['error'] });
312311
captureConsole.setup?.(mockClient);
313312

@@ -326,7 +325,7 @@ describe('CaptureConsole setup', () => {
326325
expect(mockScope.addEventProcessor).toHaveBeenCalledTimes(1);
327326

328327
expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({
329-
handled: false,
328+
handled: true,
330329
type: 'console',
331330
});
332331
});

0 commit comments

Comments
 (0)