mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Add captureWarning for warnings (#1506)
This commit is contained in:
parent
b97e1c6a38
commit
428c310d40
@ -10,13 +10,13 @@ function expandStackPortPrefix(value?: string | null) {
|
||||
return prefix ? value.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, prefix) : value;
|
||||
}
|
||||
|
||||
const sentryErrorSink = (location: string, error: unknown) => {
|
||||
const sentryErrorSink = (location: string, error: unknown, level: "error" | "warning") => {
|
||||
if (!("captureException" in Sentry)) {
|
||||
// this happens if somehow this is called outside of a Next.js script (eg. in the Prisma seed.ts), just log and ignore
|
||||
console.log("Attempted to capture Sentry error outside of Next.js script, ignoring");
|
||||
return;
|
||||
}
|
||||
Sentry.captureException(error, { extra: { location } });
|
||||
Sentry.captureException(error, { extra: { location }, level });
|
||||
runAsynchronouslyAndWaitUntil(Sentry.flush());
|
||||
};
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@ function expandStackPortPrefix(value?: string | null) {
|
||||
return prefix ? value.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, prefix as string) : value;
|
||||
}
|
||||
|
||||
const sentryErrorSink = (location: string, error: unknown) => {
|
||||
Sentry.captureException(error, { extra: { location } });
|
||||
const sentryErrorSink = (location: string, error: unknown, level: "error" | "warning") => {
|
||||
Sentry.captureException(error, { extra: { location }, level });
|
||||
};
|
||||
|
||||
export function ensurePolyfilled() {
|
||||
|
||||
@ -89,8 +89,8 @@ export function initSentry() {
|
||||
},
|
||||
});
|
||||
|
||||
registerErrorSink((location, error) => {
|
||||
Sentry.captureException(error, { extra: { location } });
|
||||
registerErrorSink((location, error, level) => {
|
||||
Sentry.captureException(error, { extra: { location }, level });
|
||||
ignoreUnhandledRejection(Sentry.flush(2000));
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,16 +94,23 @@ export function errorToNiceString(error: unknown): string {
|
||||
}
|
||||
|
||||
|
||||
const errorSinks = new Set<(location: string, error: unknown, ...extraArgs: unknown[]) => void>();
|
||||
export function registerErrorSink(sink: (location: string, error: unknown) => void): void {
|
||||
export type CaptureLevel = "error" | "warning";
|
||||
|
||||
type ErrorSink = (location: string, error: unknown, level: CaptureLevel, ...extraArgs: unknown[]) => void;
|
||||
|
||||
const errorSinks = new Set<ErrorSink>();
|
||||
export function registerErrorSink(sink: ErrorSink): void {
|
||||
if (errorSinks.has(sink)) {
|
||||
return;
|
||||
}
|
||||
errorSinks.add(sink);
|
||||
}
|
||||
registerErrorSink((location, error, ...extraArgs) => {
|
||||
console.error(
|
||||
`\x1b[41mCaptured error in ${location}:`,
|
||||
registerErrorSink((location, error, level, ...extraArgs) => {
|
||||
const logger = level === "warning" ? console.warn : console.error;
|
||||
const colorCode = level === "warning" ? "\x1b[43m" : "\x1b[41m";
|
||||
const label = level === "warning" ? "warning" : "error";
|
||||
logger(
|
||||
`${colorCode}Captured ${label} in ${location}:`,
|
||||
// HACK: Log a nicified version of the error to get around buggy Next.js pretty-printing
|
||||
// https://www.reddit.com/r/nextjs/comments/1gkxdqe/comment/m19kxgn/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
|
||||
errorToNiceString(error),
|
||||
@ -111,11 +118,22 @@ registerErrorSink((location, error, ...extraArgs) => {
|
||||
"\x1b[0m",
|
||||
);
|
||||
});
|
||||
registerErrorSink((location, error, ...extraArgs) => {
|
||||
registerErrorSink((location, error, level, ...extraArgs) => {
|
||||
globalVar.stackCapturedErrors = globalVar.stackCapturedErrors ?? [];
|
||||
globalVar.stackCapturedErrors.push({ location, error, extraArgs });
|
||||
globalVar.stackCapturedErrors.push({ location, error, level, extraArgs });
|
||||
});
|
||||
|
||||
function dispatchToSinks(location: string, error: unknown, level: CaptureLevel): void {
|
||||
for (const sink of errorSinks) {
|
||||
sink(
|
||||
location,
|
||||
error,
|
||||
level,
|
||||
...error && (typeof error === 'object' || typeof error === 'function') && "customCaptureExtraArgs" in error && Array.isArray(error.customCaptureExtraArgs) ? (error.customCaptureExtraArgs as any[]) : [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures an error and sends it to the error sinks (most notably, Sentry). Errors caught with captureError are
|
||||
* supposed to be seen by an engineer, so they should be actionable and important.
|
||||
@ -126,13 +144,15 @@ registerErrorSink((location, error, ...extraArgs) => {
|
||||
* Errors that bubble up to the top of runAsynchronously or a route handler are already captured with captureError.
|
||||
*/
|
||||
export function captureError(location: string, error: unknown): void {
|
||||
for (const sink of errorSinks) {
|
||||
sink(
|
||||
location,
|
||||
error,
|
||||
...error && (typeof error === 'object' || typeof error === 'function') && "customCaptureExtraArgs" in error && Array.isArray(error.customCaptureExtraArgs) ? (error.customCaptureExtraArgs as any[]) : [],
|
||||
);
|
||||
}
|
||||
dispatchToSinks(location, error, "error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Like captureError, but logs at warning level. Use for issues that an engineer should know about but that aren't
|
||||
* severe enough to be treated as an error (e.g. recoverable failures in background tasks).
|
||||
*/
|
||||
export function captureWarning(location: string, error: unknown): void {
|
||||
dispatchToSinks(location, error, "warning");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { isBrowserLike } from "@stackframe/stack-shared/dist/utils/env";
|
||||
import { captureWarning } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Result } from "@stackframe/stack-shared/dist/utils/results";
|
||||
|
||||
@ -255,12 +256,12 @@ export class SessionRecorder {
|
||||
);
|
||||
|
||||
if (res.status === "error") {
|
||||
console.warn("SessionRecorder flush failed:", res.error);
|
||||
captureWarning("SessionRecorder.flush", res.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.data.ok) {
|
||||
console.warn("SessionRecorder flush failed:", res.data.status, await res.data.text());
|
||||
captureWarning("SessionRecorder.flush", new Error(`SessionRecorder flush failed: ${res.data.status} ${await res.data.text()}`));
|
||||
}
|
||||
} finally {
|
||||
this._flushInProgress = false;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user