init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
import type { ErrorHub, ValueOf } from './types';
import type { LoggerFactory, Logger } from '../types';
/**
* Determines the level of logs to send to sentry.
*
*/
export const ERROR_REPORT_LEVEL = {
error: 'error',
error_warn: 'error_warn',
} as const;
type ReportLevel = ValueOf<typeof ERROR_REPORT_LEVEL>;
export class ErrorKitLoggerFactory implements LoggerFactory {
private readonly errorKit: ErrorHub;
private readonly reportLevel: ReportLevel;
constructor(errorKit: ErrorHub, reportLevel?: ReportLevel) {
this.errorKit = errorKit;
this.reportLevel = reportLevel ?? ERROR_REPORT_LEVEL.error;
}
loggerFor(name: string): Logger {
return new ErrorKitLogger(name, this.errorKit, this.reportLevel);
}
}
interface HasToString {
toString(): string;
}
export class ErrorKitLogger implements Logger {
private readonly name: string;
private readonly errorKit: ErrorHub;
private readonly reportLevel: ReportLevel;
constructor(name: string, errorKit: ErrorHub, reportLevel: ReportLevel) {
this.name = name;
this.errorKit = errorKit;
this.reportLevel = reportLevel;
}
private stringifyConsoleArgs(...args: unknown[]): string {
return args.reduce((acc: string, val: unknown) => {
let tempVal: HasToString;
switch (true) {
case val instanceof Error: {
tempVal = (val as unknown as InstanceType<typeof Error>)
.message;
break;
}
case typeof val === 'object': {
try {
tempVal = JSON.stringify(val);
} catch (e) {
tempVal = `failed to stringify ${val}`;
}
break;
}
case typeof val === 'undefined' || val === null: {
tempVal = `${val}`;
break;
}
default: {
tempVal = val as HasToString;
}
}
return `${acc} ${tempVal.toString()}`;
}, `[${this.name}]`) as string;
}
debug(..._args: unknown[]): string {
return '';
}
info(..._args: unknown[]): string {
return '';
}
warn(...args: unknown[]): string {
if (this.reportLevel === ERROR_REPORT_LEVEL.error_warn) {
this.errorKit.captureMessage(this.stringifyConsoleArgs(...args));
}
return '';
}
error(...args: unknown[]): string {
const errors = args.filter((item) => item instanceof Error) as Error[];
const message = this.stringifyConsoleArgs(...args);
const error = errors.length === 0 ? new Error(message) : errors[0];
error.message = message;
this.errorKit.captureException(error);
return '';
}
}

View File

@@ -0,0 +1,108 @@
import { Severity } from '@sentry/types';
import type { Logger, LoggerFactory } from '../types';
import type {
captureException,
captureMessage,
addBreadcrumb,
ErrorHub,
ErrorKitConfig,
} from './types';
type PartialSentryModule = {
captureException: typeof captureException;
captureMessage: typeof captureMessage;
addBreadcrumb: typeof addBreadcrumb;
};
export type ErrorKitInstance = InstanceType<typeof ErrorKit>;
export const setupErrorKit = (
config: ErrorKitConfig,
loggerFactory: LoggerFactory,
): ErrorKitInstance | undefined => {
if (typeof window === 'undefined') return;
const log = loggerFactory.loggerFor('errorkit');
const isMultiDev = window.location.href.includes('multidev');
const BUILD_ENV = process.env.NODE_ENV;
const isErrorKitEnabled = BUILD_ENV === 'production' && !isMultiDev;
const initializeErrorKit =
async (): Promise<PartialSentryModule | null> => {
let sentry: PartialSentryModule | null = null;
if (isErrorKitEnabled) {
try {
const { createSentryConfig } = await import(
'@amp-metrics/sentrykit'
);
const Sentry = await import('@sentry/browser');
Sentry.init(createSentryConfig(config));
sentry = {
addBreadcrumb: Sentry.addBreadcrumb,
captureException: Sentry.captureException,
captureMessage: Sentry.captureMessage,
};
} catch (e) {
log.error('something went wrong setting up errorKit', e);
}
}
return sentry;
};
return new ErrorKit(initializeErrorKit(), log, isErrorKitEnabled);
};
class ErrorKit implements ErrorHub {
private readonly sentry: Promise<PartialSentryModule | null>;
private readonly logger: Logger;
private readonly isErrorKitEnabled: boolean;
constructor(
sentry: Promise<PartialSentryModule | null>,
log: Logger,
isErrorKitEnabled: boolean,
) {
this.sentry = sentry;
this.logger = log;
this.isErrorKitEnabled = isErrorKitEnabled;
if (!isErrorKitEnabled) {
log.debug('errorkit is disabled');
}
}
async captureMessage(message: string) {
if (!this.isErrorKitEnabled) return;
const sentry = await this.sentry;
if (sentry) {
sentry.addBreadcrumb({
category: 'log.warn',
level: Severity.Warning,
});
sentry.captureMessage(message, Severity.Warning);
} else {
this.logger.warn(`${message} was not sent to errorKit`);
}
}
async captureException(exception: Error) {
if (!this.isErrorKitEnabled) return;
const sentry = await this.sentry;
if (sentry) {
sentry.addBreadcrumb({
type: 'error',
category: 'error',
level: Severity.Error,
});
sentry.captureException(exception);
} else {
this.logger.warn(
`The following exception was not sent to errorKit:`,
exception,
);
}
}
}