import type { LoggerService } from '@nestjs/common'; type LogLevel = 'error' | 'warn' | 'log' | 'debug'; const severity: Record = { error: 0, warn: 1, log: 2, debug: 3, }; // 按字段名递归脱敏,日志中不得出现会话、密码、密钥或授权头原文。 const sensitiveKeys = /authorization|cookie|password|secret|token|api[_-]?key/i; export class AppLogger implements LoggerService { public constructor(private readonly minimumLevel: LogLevel) {} public log(message: unknown, context?: unknown): void { this.write('log', message, context); } public error(message: unknown, stack?: string, context?: unknown): void { this.write('error', message, { stack, context }); } public warn(message: unknown, context?: unknown): void { this.write('warn', message, context); } public debug(message: unknown, context?: unknown): void { this.write('debug', message, context); } public verbose(message: unknown, context?: unknown): void { this.debug(message, context); } private write(level: LogLevel, message: unknown, context?: unknown): void { if (severity[level] > severity[this.minimumLevel]) return; const entry = { timestamp: new Date().toISOString(), level, message: typeof message === 'string' ? message : sanitize(message), context: sanitize(context), }; // 每行独立 JSON,方便日志采集系统按字段索引 request_id、状态码等上下文。 const output = JSON.stringify(entry); if (level === 'error') console.error(output); else console.log(output); } } function sanitize(value: unknown): unknown { if (Array.isArray(value)) return value.map(sanitize); if (value && typeof value === 'object') { return Object.fromEntries( Object.entries(value).map(([key, child]) => [ key, sensitiveKeys.test(key) ? '[REDACTED]' : sanitize(child), ]), ); } return value; }