NestJS 入门(10):日志——为什么常用 Winston? 上一篇NestJS 入门9连上数据库SQL 写在哪 讲了数据和表结构。服务跑起来之后排障靠的是日志哪次请求 401 了、登录失败是哪个 email、某次生成耗了多久。Nest 自带Logger本地够用。生产里 Node 社区最常见的是Winston分级、JSON、写文件/控制台、按天切割一套就能接上。1. 为什么不要满地console.logconsole.log(login ok,user);// 可能把 password 哈希打出来console.log(error,e);// 没有级别不好过滤console.log(newDate(),req.url);// 格式每次不一样机器搜不动问题很具体需求console.log正经日志库只要 error全混在 stdoutlevel: error按请求串起来没有统一字段带requestId给 ELK / Loki 采集自由文本难解析一行一条 JSON本地彩色、生产 JSON自己 iftransport 分开配所以目标不是「能打印」而是有级别、有结构、能按一次请求把前后日志对上。2. Nest 自带 Logger入门先会这个import{Injectable,Logger}fromnestjs/common;Injectable()exportclassAuthService{privatereadonlyloggernewLogger(AuthService.name);asynclogin(email:string,password:string){this.logger.log(login attempt:${email});this.logger.warn(invalid credentials);this.logger.error(token sign failed,err?.stack);}}第二个参数类名会出现在日志里方便看是哪个 Service 打的级别log/error/warn/debug/verbose够写业务、够看启动信息。不够的地方是默认偏给人看的文本切文件、JSON、按天轮转要自己拼。这时上 Winston。3. Winston 是干什么的Winston 本身和 Nest 无关是 Node 日志库。核心就三块logger.info(hello, { userId: 1 }) → format时间、JSON、颜色 → transports打到哪Console / File / 远程Nest 里通常用nest-winston把 Winston 接到 Nest 的LoggerService接口上这样new Logger(Xxx.name)仍能用app.useLogger(...)后框架自己的启动日志也走 Winstonnpmi winston nest-winston4. 接到 Nest最小可跑main.tsimport{WinstonModule}fromnest-winston;import*aswinstonfromwinston;asyncfunctionbootstrap(){constappawaitNestFactory.create(AppModule,{logger:WinstonModule.createLogger({level:process.env.LOG_LEVEL||info,format:winston.format.combine(winston.format.timestamp(),winston.format.json()),transports:[newwinston.transports.Console(),],}),});awaitapp.listen(3000);}生产一行会接近{level:info,message:API service running on http://localhost:3000,timestamp:2026-08-15T10:00:00.000Z,context:Bootstrap}采集端按level、message、timestamp建索引即可。本地开发若想看彩色文本Console 可以换成newwinston.transports.Console({format:winston.format.combine(winston.format.colorize(),winston.format.simple()),})本地给人看生产给机器看两个 transport 或按NODE_ENV分支即可。5. 业务里怎么打注入 Nest 的 loggernest-winston提供后仍用官方Logger最省事Injectable()exportclassUsersService{privatereadonlyloggernewLogger(UsersService.name);asyncfindByEmail(email:string){this.logger.debug(findByEmail${email});constuserawaitthis.users.findOne({where:{email}});if(!user){this.logger.warn(user not found:${email});}returnuser;}}也可以做成全局模块注入自己的封装和第六篇Global()同一套路统一带上service: apithis.logger.info(POST /api/auth/login 200,{requestId,method:POST,path:/api/auth/login,statusCode:200,durationMs:12,});字段尽量固定requestId、method、path、statusCode、durationMs、error。后面搜「这次请求慢在哪」才搜得到。6. 请求级日志Interceptor 打出入第四篇的拦截器管信封日志也可以挂一个全局 Interceptor在请求结束时打一行INFO GET /api/projects 200 durationMs35 requestId1723-ab12 ERROR POST /api/auth/login 500 durationMs8 requestId1723-cd99 error...要点进来时生成requestId或读网关的x-request-id写进req响应头带上X-Request-Id方便前端/网关对照业务日志都带上同一个requestId没有requestId时十条user not found对不上是哪次点击。7. 级别怎么用级别什么时候error失败了需要人看连库失败、未捕获异常warn不正常但还能继续校验失败、降级、token 过期info正常关键路径启动成功、请求进出可抽样debug开发细节SQL、中间变量生产默认关掉生产LOG_LEVELinfo即可。debug全开会把磁盘和费用打爆也更容易把敏感信息带出去。8. 写文件、按天切知道即可newwinston.transports.File({filename:logs/error.log,level:error}),newwinston.transports.File({filename:logs/combined.log}),Docker / K8s 里更常见的是只打 stdout JSON由平台采集应用自己不落盘。自己管文件时再加winston-daily-rotate-file按天切割、限制保留天数。入门先 Console JSON等真正部署再决定文件还是 stdout。9. 不要记进日志的东西密码、token、cookie、API Key身份证号、完整银行卡超大 body / embedding 向量登录失败打email可以打password不行。Authorization: Bearer ...不要整段进日志。10. 和「自己包一层 console」的关系有的项目不引入 Winston而是console.log(JSON.stringify({level:info,message,service:api,timestamp:newDate().toISOString(),requestId,}));结构也对采集也能吃。缺的是transport、按级别分文件、和 Nest 内置 Logger 打通、生态里现成的 rotate。小项目可以自制 JSON console要分级、切文件、统一框架日志再上 Winston。换库不换习惯级别 结构化字段 requestId。11. 小结日志要分级、要结构不要散落console.logNestLogger够入门生产常用Winston nest-winston一行 JSONlevel/message/timestamp/requestId请求进出适合全局 Interceptor业务细节打在 Service生产默认info密码和 token 永远不要进日志对照本系列分层、注入、模块、连库清楚了吗统一信封和异常 Filter 能对上日志里的 status 吗这条日志有级别吗有 requestId 吗会不会把密钥打出去系列导航上一篇NestJS 入门9连上数据库SQL 写在哪第八篇NestJS 入门8环境变量与配置第七篇NestJS 入门7生命周期钩子第六篇NestJS 入门6Module 边界与导出第五篇NestJS 入门5Pipe 与 DTO 校验第四篇NestJS 入门4统一响应与异常处理第三篇NestJS 入门3Guard 如何挡住未登录请求第二篇NestJS 入门2依赖注入到底解决了什么问题第一篇NestJS 入门1先搞懂 Module、Controller、Service