import pino from 'pino'; import { getRequestContext } from '@/lib/request-context'; export const logger = pino({ level: process.env.LOG_LEVEL ?? 'info', /** * Mix the active request context (request id, port id, user id) into * EVERY log line emitted within an API request — this is what makes * the super-admin error inspector usable: paste a request id into the * search and every log line that fired during that request comes back * keyed to it. Outside a request (queue jobs, scheduled tasks) the * mixin returns an empty object so the log line is unchanged. */ mixin() { const ctx = getRequestContext(); if (!ctx) return {}; return { requestId: ctx.requestId, portId: ctx.portId || undefined, userId: ctx.userId || undefined, }; }, redact: { paths: [ 'password', 'token', 'secret', 'accessKey', 'secretKey', 'creditCard', '*.password', '*.token', '*.secret', '*.accessKey', '*.secretKey', // Encrypted credential blobs surface in storage / smtp config logs // unintentionally; redact them defensively even though they're // already AES-encrypted at rest. '*.secretKeyEncrypted', '*.smtpPassEncrypted', '*.imapPassEncrypted', '*.proxyHmacSecretEncrypted', // HTTP authorization headers (Bearer tokens, Basic creds) leak via // err.config.headers on http-client error logs. '*.headers.authorization', '*.headers.Authorization', '*.headers["x-documenso-secret"]', '*.config.headers.Authorization', '*.config.headers.authorization', // Cookie headers can carry session tokens. '*.headers.cookie', '*.headers.Cookie', // Two-level nesting for things like `req.headers.authorization` or // `cfg.s3.secretKeyEncrypted`. '*.*.password', '*.*.token', '*.*.secret', '*.*.secretKeyEncrypted', '*.*.headers.authorization', '*.*.headers.Authorization', ], censor: '[REDACTED]', }, transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty', options: { colorize: true } } : undefined, serializers: { err: pino.stdSerializers.err, req: pino.stdSerializers.req, res: pino.stdSerializers.res, }, });