import { AsyncLocalStorage } from 'node:async_hooks'; import pino from 'pino'; import pinoPretty from 'pino-pretty'; type LoggerEnvironment = { nodeEnv?: string; }; type RequestLogContext = { requestId: string; }; const requestLogContext = new AsyncLocalStorage(); const prettyOptions = { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname', }; const getLoggerEnvironment = (): LoggerEnvironment => ({ nodeEnv: process.env.NODE_ENV, }); const isDevelopmentEnvironment = (environment: LoggerEnvironment) => environment.nodeEnv !== 'production'; const isTestEnvironment = (environment: LoggerEnvironment) => environment.nodeEnv === 'test'; export const runWithRequestLogContext = (requestId: string, callback: () => T): T => requestLogContext.run({ requestId }, callback); const getRequestLogMetadata = () => { const context = requestLogContext.getStore(); if (!context) { return {}; } return { requestId: context.requestId }; }; export const createLoggerOptions = (environment: LoggerEnvironment): pino.LoggerOptions => ({ enabled: !isTestEnvironment(environment), level: isDevelopmentEnvironment(environment) ? 'debug' : 'info', mixin: () => getRequestLogMetadata(), formatters: { level(label, number) { return { level: number, severity: label.toUpperCase(), }; }, }, }); export const createLogger = ( environment: LoggerEnvironment = getLoggerEnvironment(), ) => { const options = createLoggerOptions(environment); if (isTestEnvironment(environment)) { return pino({ ...options, level: 'silent' }); } if (isDevelopmentEnvironment(environment)) { return pino(options, pinoPretty(prettyOptions)); } return pino(options); }; export const logger = createLogger();