refactor(logger): extract sentry reporter
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
import * as Sentry from "@sentry/nextjs";
|
||||||
|
import type { CaptureContext, SeverityLevel } from "@sentry/nextjs";
|
||||||
|
|
||||||
|
export type RemoteLogLevel = "warn" | "error" | "fatal";
|
||||||
|
|
||||||
|
export interface SentryLogPayload {
|
||||||
|
level: RemoteLogLevel;
|
||||||
|
component: string;
|
||||||
|
message: string;
|
||||||
|
data: unknown[];
|
||||||
|
formatValue: (value: unknown) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reportSentryLog(payload: SentryLogPayload): void {
|
||||||
|
try {
|
||||||
|
const error = findError(payload.data);
|
||||||
|
const captureContext = toSentryCaptureContext(payload);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
Sentry.captureException(error, captureContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sentry.captureMessage(
|
||||||
|
payload.message ||
|
||||||
|
`[${payload.component}] ${payload.level.toUpperCase()} log`,
|
||||||
|
captureContext,
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// Logging must never break the app runtime.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSentryCaptureContext({
|
||||||
|
level,
|
||||||
|
component,
|
||||||
|
message,
|
||||||
|
data,
|
||||||
|
formatValue,
|
||||||
|
}: SentryLogPayload): CaptureContext {
|
||||||
|
return {
|
||||||
|
level: toSentrySeverityLevel(level),
|
||||||
|
tags: {
|
||||||
|
component,
|
||||||
|
log_level: level,
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
component,
|
||||||
|
message,
|
||||||
|
data: data.map((item) => formatValue(item)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSentrySeverityLevel(level: RemoteLogLevel): SeverityLevel {
|
||||||
|
return level === "warn" ? "warning" : level;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findError(args: unknown[]): Error | null {
|
||||||
|
return args.find((arg): arg is Error => arg instanceof Error) ?? null;
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import pino, { type Logger as PinoLogger, type LoggerOptions } from "pino";
|
import pino, { type Logger as PinoLogger, type LoggerOptions } from "pino";
|
||||||
|
|
||||||
import { AppEnvUtil } from "./app-env";
|
import { AppEnvUtil } from "./app-env";
|
||||||
|
import {
|
||||||
|
reportSentryLog,
|
||||||
|
type RemoteLogLevel,
|
||||||
|
} from "./logger-sentry-reporter";
|
||||||
|
|
||||||
type LogArgs = unknown[];
|
type LogArgs = unknown[];
|
||||||
type PinoLogArgs = Parameters<PinoLogger["debug"]>;
|
type PinoLogArgs = Parameters<PinoLogger["debug"]>;
|
||||||
@@ -133,6 +137,8 @@ export class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private write(level: LogLevel, args: LogArgs): void {
|
private write(level: LogLevel, args: LogArgs): void {
|
||||||
|
Logger.reportImportantProductionLog(level, this.component, args);
|
||||||
|
|
||||||
if (Logger.shouldUseBrowserConsole()) {
|
if (Logger.shouldUseBrowserConsole()) {
|
||||||
Logger.writeBrowserConsole(level, this.component, args);
|
Logger.writeBrowserConsole(level, this.component, args);
|
||||||
return;
|
return;
|
||||||
@@ -161,6 +167,31 @@ export class Logger {
|
|||||||
return typeof window !== "undefined" && AppEnvUtil.canOutputLogs();
|
return typeof window !== "undefined" && AppEnvUtil.canOutputLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static shouldReportToRemote(level: LogLevel): level is RemoteLogLevel {
|
||||||
|
return AppEnvUtil.isProduction() && (
|
||||||
|
level === "warn" ||
|
||||||
|
level === "error" ||
|
||||||
|
level === "fatal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static reportImportantProductionLog(
|
||||||
|
level: LogLevel,
|
||||||
|
component: string,
|
||||||
|
args: LogArgs,
|
||||||
|
): void {
|
||||||
|
if (!Logger.shouldReportToRemote(level)) return;
|
||||||
|
|
||||||
|
const { message, data } = Logger.toBrowserConsolePayload(args);
|
||||||
|
reportSentryLog({
|
||||||
|
level,
|
||||||
|
component,
|
||||||
|
message,
|
||||||
|
data,
|
||||||
|
formatValue: Logger.formatValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static writeBrowserConsole(
|
private static writeBrowserConsole(
|
||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
component: string,
|
component: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user