init commit
This commit is contained in:
93
shared/logger/src/errorkit/errorkit-logger.ts
Normal file
93
shared/logger/src/errorkit/errorkit-logger.ts
Normal 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 '';
|
||||
}
|
||||
}
|
||||
108
shared/logger/src/errorkit/errorkit.ts
Normal file
108
shared/logger/src/errorkit/errorkit.ts
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user