73 lines
1.8 KiB
JavaScript
73 lines
1.8 KiB
JavaScript
// Optional Sentry integration. This module must be safe when @sentry/node is
|
|
// not installed or SENTRY_DSN is unset; error reporting should never prevent the
|
|
// browser server from starting.
|
|
|
|
let sentry = null;
|
|
let initPromise = null;
|
|
let enabled = false;
|
|
|
|
export function initSentry(config = {}) {
|
|
const dsn = config.sentryDsn || config.SENTRY_DSN || process.env.SENTRY_DSN || '';
|
|
if (!dsn) return;
|
|
|
|
enabled = true;
|
|
initPromise = import('@sentry/node')
|
|
.then((mod) => {
|
|
sentry = mod;
|
|
sentry.init({
|
|
dsn,
|
|
environment: config.nodeEnv || process.env.NODE_ENV || 'development',
|
|
release: config.version ? `camofox-browser@${config.version}` : undefined,
|
|
});
|
|
})
|
|
.catch(() => {
|
|
enabled = false;
|
|
sentry = null;
|
|
});
|
|
}
|
|
|
|
export function captureException(error, context = {}) {
|
|
if (!enabled || !error) return;
|
|
if (!sentry) {
|
|
initPromise?.then(() => captureException(error, context)).catch(() => {});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
sentry.withScope((scope) => {
|
|
for (const [key, value] of Object.entries(context || {})) {
|
|
if (key === 'userId' && value != null) {
|
|
scope.setUser({ id: String(value) });
|
|
} else {
|
|
scope.setExtra(key, value);
|
|
}
|
|
}
|
|
sentry.captureException(error);
|
|
});
|
|
} catch {}
|
|
}
|
|
|
|
export function setupExpressErrorHandler(app) {
|
|
if (!app) return;
|
|
app.use((err, req, res, next) => {
|
|
captureException(err, {
|
|
path: req?.originalUrl,
|
|
method: req?.method,
|
|
userId: req?.query?.userId || req?.body?.userId,
|
|
reqId: req?.reqId,
|
|
});
|
|
next(err);
|
|
});
|
|
}
|
|
|
|
export async function flush(timeoutMs = 2000) {
|
|
if (!enabled) return false;
|
|
try {
|
|
if (initPromise) await initPromise;
|
|
if (!sentry?.flush) return false;
|
|
return await sentry.flush(timeoutMs);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|