CI / check (push) Successful in 28s
CI / tests-ui (push) Failing after 32s
CI / tests-unit (push) Successful in 1m43s
CI / tests-quality (push) Successful in 1m14s
CI / tests-integration (push) Successful in 1m50s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / tests-imaging-browsers (push) Successful in 1m56s
- Replace all 39 console.* calls with universal logger (pino server, console browser) - Set server-only marker on import-badge.ts - Disable DB query logging in production - Add bulkInsertCatalogItems: one transaction instead of N per-item inserts - Add reserveCatalogItemIds helper for bulk ID allocation - Optimize translateCatalogItems: single inArray batch query instead of N per-item selects - Test mock updated for logger.debug - biome clean, tsc clean, 3569 tests passing
96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
import pino from "pino";
|
|
import { env } from "@/env";
|
|
import { captureCmsError } from "./error-monitor";
|
|
import { getOperationContext } from "./foundation/request-context";
|
|
|
|
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
|
|
const isBrowser =
|
|
typeof window !== "undefined" && typeof window.console !== "undefined";
|
|
|
|
const level: LogLevel =
|
|
(env.LOG_LEVEL as LogLevel | undefined) ??
|
|
(env.NODE_ENV === "production" ? "info" : "debug");
|
|
|
|
const isProd = env.NODE_ENV === "production";
|
|
const isTest = env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
|
|
const pinoLogger = pino({
|
|
level,
|
|
base: { service: "atomcms-next" },
|
|
...(isProd || isTest
|
|
? {}
|
|
: {
|
|
transport: {
|
|
target: "pino-pretty",
|
|
options: { colorize: true, translateTime: "SYS:standard" },
|
|
},
|
|
}),
|
|
});
|
|
|
|
let requestIdCounter = 0;
|
|
|
|
export function generateRequestId(): string {
|
|
requestIdCounter += 1;
|
|
return `${Date.now().toString(36)}-${requestIdCounter.toString(36)}`;
|
|
}
|
|
|
|
function formatMeta(meta: Record<string, unknown> = {}): string {
|
|
const keys = Object.keys(meta);
|
|
if (keys.length === 0) return "";
|
|
return keys
|
|
.map((k) => {
|
|
const v = meta[k];
|
|
return `${k}=${v instanceof Error ? v.message : String(v)}`;
|
|
})
|
|
.join(" ");
|
|
}
|
|
|
|
/** Universal logger: pino on server, console in browser. Same interface everywhere. */
|
|
export const logger = {
|
|
debug(message: string, meta: Record<string, unknown> = {}): void {
|
|
if (isBrowser) {
|
|
console.debug(`[debug] ${message} ${formatMeta(meta)}`.trim());
|
|
return;
|
|
}
|
|
pinoLogger.debug({ ...meta, ...getOperationContext() }, message);
|
|
},
|
|
info(message: string, meta: Record<string, unknown> = {}): void {
|
|
if (isBrowser) {
|
|
console.info(`[info] ${message} ${formatMeta(meta)}`.trim());
|
|
return;
|
|
}
|
|
pinoLogger.info({ ...meta, ...getOperationContext() }, message);
|
|
},
|
|
warn(message: string, meta: Record<string, unknown> = {}): void {
|
|
if (isBrowser) {
|
|
console.warn(`[warn] ${message} ${formatMeta(meta)}`.trim());
|
|
return;
|
|
}
|
|
pinoLogger.warn({ ...meta, ...getOperationContext() }, message);
|
|
},
|
|
error(message: string, meta: Record<string, unknown> = {}): string {
|
|
if (isBrowser) {
|
|
console.error(`[error] ${message} ${formatMeta(meta)}`.trim());
|
|
return "";
|
|
}
|
|
const error =
|
|
meta.err instanceof Error
|
|
? meta.err
|
|
: meta.error instanceof Error
|
|
? meta.error
|
|
: new Error(
|
|
typeof meta.err === "string"
|
|
? meta.err
|
|
: typeof meta.error === "string"
|
|
? meta.error
|
|
: message,
|
|
);
|
|
const operation = getOperationContext();
|
|
const context = { ...operation, ...meta, ...operation };
|
|
const errorId = captureCmsError("server", message, error, context);
|
|
pinoLogger.error({ ...context, errorId }, message);
|
|
return errorId;
|
|
},
|
|
};
|