refactor(logger): extract sentry reporter

This commit is contained in:
2026-06-30 11:24:13 +08:00
parent a761917669
commit 345c5f7f25
2 changed files with 92 additions and 0 deletions
+61
View File
@@ -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;
}
+31
View File
@@ -1,6 +1,10 @@
import pino, { type Logger as PinoLogger, type LoggerOptions } from "pino";
import { AppEnvUtil } from "./app-env";
import {
reportSentryLog,
type RemoteLogLevel,
} from "./logger-sentry-reporter";
type LogArgs = unknown[];
type PinoLogArgs = Parameters<PinoLogger["debug"]>;
@@ -133,6 +137,8 @@ export class Logger {
}
private write(level: LogLevel, args: LogArgs): void {
Logger.reportImportantProductionLog(level, this.component, args);
if (Logger.shouldUseBrowserConsole()) {
Logger.writeBrowserConsole(level, this.component, args);
return;
@@ -161,6 +167,31 @@ export class Logger {
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(
level: LogLevel,
component: string,