Spaces:
Paused
Paused
| 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 {}; | |
| }, | |
| }); | |