umami, glitchtip, redis
Some checks failed
Build & Deploy / deploy (push) Failing after 3m45s

This commit is contained in:
2026-01-18 15:37:51 +01:00
parent 619b699f14
commit b05a21350c
29 changed files with 3568 additions and 316 deletions

View File

@@ -0,0 +1,16 @@
export type ErrorReportingUser = {
id?: string;
email?: string;
username?: string;
};
export type ErrorReportingLevel = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'log';
export interface ErrorReportingService {
captureException(error: unknown, context?: Record<string, unknown>): string | undefined;
captureMessage(message: string, level?: ErrorReportingLevel): string | undefined;
setUser(user: ErrorReportingUser | null): void;
setTag(key: string, value: string): void;
withScope<T>(fn: () => T, context?: Record<string, unknown>): T;
}

View File

@@ -0,0 +1,53 @@
import * as Sentry from '@sentry/nextjs';
import type {
ErrorReportingLevel,
ErrorReportingService,
ErrorReportingUser,
} from './error-reporting-service';
type SentryLike = typeof Sentry;
export type GlitchtipErrorReportingServiceOptions = {
enabled: boolean;
};
// GlitchTip speaks the Sentry protocol; @sentry/nextjs can send to GlitchTip via DSN.
export class GlitchtipErrorReportingService implements ErrorReportingService {
constructor(
private readonly options: GlitchtipErrorReportingServiceOptions,
private readonly sentry: SentryLike = Sentry
) {}
captureException(error: unknown, context?: Record<string, unknown>) {
if (!this.options.enabled) return undefined;
return this.sentry.captureException(error, context as any) as any;
}
captureMessage(message: string, level: ErrorReportingLevel = 'error') {
if (!this.options.enabled) return undefined;
return this.sentry.captureMessage(message, level as any) as any;
}
setUser(user: ErrorReportingUser | null) {
if (!this.options.enabled) return;
this.sentry.setUser(user as any);
}
setTag(key: string, value: string) {
if (!this.options.enabled) return;
this.sentry.setTag(key, value);
}
withScope<T>(fn: () => T, context?: Record<string, unknown>) {
if (!this.options.enabled) return fn();
return this.sentry.withScope((scope) => {
if (context) {
for (const [key, value] of Object.entries(context)) {
scope.setExtra(key, value);
}
}
return fn();
});
}
}

View File

@@ -0,0 +1,18 @@
import type { ErrorReportingLevel, ErrorReportingService, ErrorReportingUser } from './error-reporting-service';
export class NoopErrorReportingService implements ErrorReportingService {
captureException(_error: unknown, _context?: Record<string, unknown>) {
return undefined;
}
captureMessage(_message: string, _level?: ErrorReportingLevel) {
return undefined;
}
setUser(_user: ErrorReportingUser | null) {}
setTag(_key: string, _value: string) {}
withScope<T>(fn: () => T, _context?: Record<string, unknown>) {
return fn();
}
}