feat: add backend logger matching frontend text format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,17 +13,14 @@ import { schema } from "./graphql/index";
|
||||
import { buildContext } from "./graphql/context";
|
||||
import { db } from "./db/connection";
|
||||
import { redis } from "./lib/auth";
|
||||
import { logger } from "./lib/logger";
|
||||
|
||||
const PORT = parseInt(process.env.PORT || "4000");
|
||||
const UPLOAD_DIR = process.env.UPLOAD_DIR || "/data/uploads";
|
||||
const CORS_ORIGIN = process.env.CORS_ORIGIN || "http://localhost:3000";
|
||||
|
||||
async function main() {
|
||||
const fastify = Fastify({
|
||||
logger: {
|
||||
level: process.env.LOG_LEVEL || "info",
|
||||
},
|
||||
});
|
||||
const fastify = Fastify({ loggerInstance: logger });
|
||||
|
||||
await fastify.register(fastifyCookie, {
|
||||
secret: process.env.COOKIE_SECRET || "change-me-in-production",
|
||||
|
||||
91
packages/backend/src/lib/logger.ts
Normal file
91
packages/backend/src/lib/logger.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
||||
|
||||
const LEVEL_VALUES: Record<LogLevel, number> = {
|
||||
trace: 10,
|
||||
debug: 20,
|
||||
info: 30,
|
||||
warn: 40,
|
||||
error: 50,
|
||||
fatal: 60,
|
||||
};
|
||||
|
||||
function createLogger(bindings: Record<string, unknown> = {}, initialLevel: LogLevel = "info") {
|
||||
let currentLevel = initialLevel;
|
||||
|
||||
function shouldLog(level: LogLevel): boolean {
|
||||
return LEVEL_VALUES[level] >= LEVEL_VALUES[currentLevel];
|
||||
}
|
||||
|
||||
function formatMessage(level: LogLevel, arg: unknown, msg?: string): string {
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
let message: string;
|
||||
const meta: Record<string, unknown> = { ...bindings };
|
||||
|
||||
if (typeof arg === "string") {
|
||||
message = arg;
|
||||
} else if (arg !== null && typeof arg === "object") {
|
||||
// Pino-style: log(obj, msg?) — strip internal pino keys
|
||||
const { msg: m, level: _l, time: _t, pid: _p, hostname: _h, req: _req, res: _res, reqId, ...rest } = arg as Record<string, unknown>;
|
||||
message = msg || (typeof m === "string" ? m : "");
|
||||
if (reqId) meta.reqId = reqId;
|
||||
Object.assign(meta, rest);
|
||||
} else {
|
||||
message = String(arg ?? "");
|
||||
}
|
||||
|
||||
const parts = [`[${timestamp}]`, `[${level.toUpperCase()}]`, message];
|
||||
let result = parts.join(" ");
|
||||
|
||||
const metaEntries = Object.entries(meta).filter(([k]) => k !== "reqId");
|
||||
const reqId = meta.reqId;
|
||||
if (reqId) result = `[${timestamp}] [${level.toUpperCase()}] [${reqId}] ${message}`;
|
||||
|
||||
if (metaEntries.length > 0) {
|
||||
result += " " + JSON.stringify(Object.fromEntries(metaEntries));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function write(level: LogLevel, arg: unknown, msg?: string) {
|
||||
if (!shouldLog(level)) return;
|
||||
const formatted = formatMessage(level, arg, msg);
|
||||
switch (level) {
|
||||
case "trace":
|
||||
case "debug":
|
||||
console.debug(formatted);
|
||||
break;
|
||||
case "info":
|
||||
console.info(formatted);
|
||||
break;
|
||||
case "warn":
|
||||
console.warn(formatted);
|
||||
break;
|
||||
case "error":
|
||||
case "fatal":
|
||||
console.error(formatted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
get level() {
|
||||
return currentLevel;
|
||||
},
|
||||
set level(l: string) {
|
||||
currentLevel = l as LogLevel;
|
||||
},
|
||||
trace: (arg: unknown, msg?: string) => write("trace", arg, msg),
|
||||
debug: (arg: unknown, msg?: string) => write("debug", arg, msg),
|
||||
info: (arg: unknown, msg?: string) => write("info", arg, msg),
|
||||
warn: (arg: unknown, msg?: string) => write("warn", arg, msg),
|
||||
error: (arg: unknown, msg?: string) => write("error", arg, msg),
|
||||
fatal: (arg: unknown, msg?: string) => write("fatal", arg, msg),
|
||||
silent: () => {},
|
||||
child: (newBindings: Record<string, unknown>) =>
|
||||
createLogger({ ...bindings, ...newBindings }, currentLevel),
|
||||
};
|
||||
}
|
||||
|
||||
export const logger = createLogger({}, (process.env.LOG_LEVEL as LogLevel) || "info");
|
||||
Reference in New Issue
Block a user