Skip to content

Commit 6d4aa95

Browse files
authored
fix(core): Report errors to browser global error handler (#5029)
This allows tools such as Qwik Insights, Sentry or New Relic to capture errors and report them to server. Fix #2618 getsentry/sentry-javascript#8867 (comment)
1 parent ab9f97c commit 6d4aa95

File tree

3 files changed

+34
-20
lines changed

3 files changed

+34
-20
lines changed

packages/qwik/src/core/error/assert.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { QwikElement, VirtualElement } from '../render/dom/virtual-element';
22
import { isElement, isQwikElement } from '../util/element';
3-
import { logErrorAndStop } from '../util/log';
3+
import { throwErrorAndStop } from '../util/log';
44
import { qDev } from '../util/qdev';
55

66
const ASSERT_DISCLAIMER = 'Internal assert, this is likely caused by a bug in Qwik: ';
@@ -14,7 +14,7 @@ export function assertDefined<T>(
1414
if (value != null) {
1515
return;
1616
}
17-
throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
17+
throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
1818
}
1919
}
2020

@@ -28,14 +28,14 @@ export function assertEqual(
2828
if (value1 === value2) {
2929
return;
3030
}
31-
throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
31+
throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
3232
}
3333
}
3434

3535
export function assertFail(text: string, ...parts: any[]): never;
3636
export function assertFail(text: string, ...parts: any[]) {
3737
if (qDev) {
38-
throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
38+
throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
3939
}
4040
}
4141

@@ -44,7 +44,7 @@ export function assertTrue(value1: any, text: string, ...parts: any[]): asserts
4444
if (value1 === true) {
4545
return;
4646
}
47-
throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
47+
throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
4848
}
4949
}
5050

@@ -53,7 +53,7 @@ export function assertNumber(value1: any, text: string, ...parts: any[]): assert
5353
if (typeof value1 === 'number') {
5454
return;
5555
}
56-
throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
56+
throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
5757
}
5858
}
5959

@@ -62,15 +62,15 @@ export function assertString(value1: any, text: string, ...parts: any[]): assert
6262
if (typeof value1 === 'string') {
6363
return;
6464
}
65-
throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
65+
throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts);
6666
}
6767
}
6868

6969
export function assertQwikElement(el: any): asserts el is QwikElement {
7070
if (qDev) {
7171
if (!isQwikElement(el)) {
7272
console.error('Not a Qwik Element, got', el);
73-
throw logErrorAndStop(ASSERT_DISCLAIMER + 'Not a Qwik Element');
73+
throwErrorAndStop(ASSERT_DISCLAIMER + 'Not a Qwik Element');
7474
}
7575
}
7676
}
@@ -79,7 +79,7 @@ export function assertElement(el: Node | VirtualElement): asserts el is Element
7979
if (qDev) {
8080
if (!isElement(el)) {
8181
console.error('Not a Element, got', el);
82-
throw logErrorAndStop(ASSERT_DISCLAIMER + 'Not an Element');
82+
throwErrorAndStop(ASSERT_DISCLAIMER + 'Not an Element');
8383
}
8484
}
8585
}

packages/qwik/src/core/state/common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '../use/use-task';
1414
import type { QwikElement } from '../render/dom/virtual-element';
1515
import { notifyChange } from '../render/dom/notify-render';
16-
import { createError, logError } from '../util/log';
16+
import { logError, throwErrorAndStop } from '../util/log';
1717
import { tryGetContext } from './context';
1818
import { QObjectFlagsSymbol, QObjectManagerSymbol, QOjectTargetSymbol } from './constants';
1919
import type { Signal } from './signal';
@@ -101,7 +101,7 @@ const _verifySerializable = <T>(value: T, seen: Set<any>, ctx: string, preMessag
101101
)});\n\nPlease check out https://qwik.builder.io/docs/advanced/qrl/ for more information.`;
102102
}
103103
console.error('Trying to serialize', value);
104-
throw createError(message);
104+
throwErrorAndStop(message);
105105
}
106106
return value;
107107
};

packages/qwik/src/core/util/log.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
import type { QwikElement } from '../render/dom/virtual-element';
22
import type { QContext } from '../state/context';
33
import { isElement, isNode } from './element';
4-
import { qDev } from './qdev';
4+
import { qDev, qTest } from './qdev';
55

66
const STYLE = qDev
77
? `background: #564CE0; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`
88
: '';
99

1010
export const logError = (message?: any, ...optionalParams: any[]) => {
11-
const err = message instanceof Error ? message : createError(message);
12-
const messageStr = err.stack || err.message;
13-
console.error('%cQWIK ERROR', STYLE, messageStr, ...printParams(optionalParams));
14-
return err;
11+
return createAndLogError(true, message, ...optionalParams);
1512
};
1613

17-
export const createError = (message?: string) => {
18-
const err = new Error(message);
19-
return err;
14+
export const throwErrorAndStop = (message?: any, ...optionalParams: any[]): never => {
15+
const error = createAndLogError(false, message, ...optionalParams);
16+
// eslint-disable-next-line no-debugger
17+
debugger;
18+
throw error;
2019
};
2120

2221
export const logErrorAndStop = (message?: any, ...optionalParams: any[]) => {
23-
const err = logError(message, ...optionalParams);
22+
const err = createAndLogError(true, message, ...optionalParams);
2423
// eslint-disable-next-line no-debugger
2524
debugger;
2625
return err;
@@ -79,3 +78,18 @@ const printElement = (el: Element) => {
7978
ctx: isServer ? undefined : ctx,
8079
};
8180
};
81+
82+
const createAndLogError = (asyncThrow: boolean, message?: any, ...optionalParams: any[]) => {
83+
const err = message instanceof Error ? message : new Error(message);
84+
const messageStr = err.stack || err.message;
85+
console.error('%cQWIK ERROR', STYLE, messageStr, ...printParams(optionalParams));
86+
asyncThrow &&
87+
!qTest &&
88+
setTimeout(() => {
89+
// throwing error asynchronously to avoid breaking the current call stack.
90+
// We throw so that the error is delivered to the global error handler for
91+
// reporting it to a third-party tools such as Qwik Insights, Sentry or New Relic.
92+
throw err;
93+
}, 0);
94+
return err;
95+
};

0 commit comments

Comments
 (0)