91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import path from "node:path";
|
|
import fs from "node:fs";
|
|
import pino from "pino";
|
|
import { pinoHttp } from "pino-http";
|
|
import { readConfigFile } from "../config-file.js";
|
|
import { resolveDefaultLogsDir, resolveHomeAwarePath } from "../home-paths.js";
|
|
|
|
function resolveServerLogDir(): string {
|
|
const envOverride = process.env.PAPERCLIP_LOG_DIR?.trim();
|
|
if (envOverride) return resolveHomeAwarePath(envOverride);
|
|
|
|
const fileLogDir = readConfigFile()?.logging.logDir?.trim();
|
|
if (fileLogDir) return resolveHomeAwarePath(fileLogDir);
|
|
|
|
return resolveDefaultLogsDir();
|
|
}
|
|
|
|
const logDir = resolveServerLogDir();
|
|
fs.mkdirSync(logDir, { recursive: true });
|
|
|
|
const logFile = path.join(logDir, "server.log");
|
|
|
|
const sharedOpts = {
|
|
translateTime: "HH:MM:ss",
|
|
ignore: "pid,hostname",
|
|
singleLine: true,
|
|
};
|
|
|
|
export const logger = pino({
|
|
level: "debug",
|
|
}, pino.transport({
|
|
targets: [
|
|
{
|
|
target: "pino-pretty",
|
|
options: { ...sharedOpts, ignore: "pid,hostname,req,res,responseTime", colorize: true, destination: 1 },
|
|
level: "info",
|
|
},
|
|
{
|
|
target: "pino-pretty",
|
|
options: { ...sharedOpts, colorize: false, destination: logFile, mkdir: true },
|
|
level: "debug",
|
|
},
|
|
],
|
|
}));
|
|
|
|
export const httpLogger = pinoHttp({
|
|
logger,
|
|
customLogLevel(_req, res, err) {
|
|
if (err || res.statusCode >= 500) return "error";
|
|
if (res.statusCode >= 400) return "warn";
|
|
return "info";
|
|
},
|
|
customSuccessMessage(req, res) {
|
|
return `${req.method} ${req.url} ${res.statusCode}`;
|
|
},
|
|
customErrorMessage(req, res, err) {
|
|
const ctx = (res as any).__errorContext;
|
|
const errMsg = ctx?.error?.message || err?.message || (res as any).err?.message || "unknown error";
|
|
return `${req.method} ${req.url} ${res.statusCode} — ${errMsg}`;
|
|
},
|
|
customProps(req, res) {
|
|
if (res.statusCode >= 400) {
|
|
const ctx = (res as any).__errorContext;
|
|
if (ctx) {
|
|
return {
|
|
errorContext: ctx.error,
|
|
reqBody: ctx.reqBody,
|
|
reqParams: ctx.reqParams,
|
|
reqQuery: ctx.reqQuery,
|
|
};
|
|
}
|
|
const props: Record<string, unknown> = {};
|
|
const { body, params, query } = req as any;
|
|
if (body && typeof body === "object" && Object.keys(body).length > 0) {
|
|
props.reqBody = body;
|
|
}
|
|
if (params && typeof params === "object" && Object.keys(params).length > 0) {
|
|
props.reqParams = params;
|
|
}
|
|
if (query && typeof query === "object" && Object.keys(query).length > 0) {
|
|
props.reqQuery = query;
|
|
}
|
|
if ((req as any).route?.path) {
|
|
props.routePath = (req as any).route.path;
|
|
}
|
|
return props;
|
|
}
|
|
return {};
|
|
},
|
|
});
|