为什么 Node 要用 Winston 打印日志?

Node 里怎么打印日志呢?

有同学说,不也是用 console.log 么。

不,服务端打印日志一般不会用 console.log。

因为 console.log 打印完就没了,而服务端的日志经常要用来排查问题,需要搜索、分析日志内容,所以需要写入文件或者数据库里。

而且打印的日志需要分级别,比如有的是错误的日志,有的只是普通日志,需要能够过滤不同级别的日志。

此外,打印的日志需要带上时间戳,所在的代码位置等信息。

这些都是 console.log 没有的功能。

所以我们一般都会用专门的日志框架来做,比如 winston。

它是 Node 最流行的日志框架,npm 官网上可以看到每周千万级的下载量:

那 winston 都有什么功能?怎么用呢?

我们试试看:

bash 复制代码
mkdir winston-test
cd winston-test
npm init -y

先创建个项目。

安装 winston:

css 复制代码
npm install --save winston

然后写下 index.js

javascript 复制代码
import winston from 'winston';

const logger = winston.createLogger({
    level: 'debug',
    format: winston.format.simple(),
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ 
            dirname: 'log', filename: 'test.log' 
        }),
    ]
});

logger.info('光光光光光光光光光');
logger.error('东东东东东东东东');
logger.debug(66666666);

用 createLogger 创建了 logger 实例,指定 level、format、tranports。

level:打印的日志级别

format:日志格式

transports:日志的传输方式

我们指定了 Console 和 File 两种传输方式。

在 package.json 里指定 type 为 module,也就是所有代码都是 es module 的:

这样代码里就可以直接用 import、export 这些语法了。

用 node 跑一下:

复制代码
node index.js

可以看到控制台和文件里都有了打印的日志。

再跑一遍:

复制代码
node index.js

会在后面追加:

那么问题来了,如果所有日志都写在一个文件里,那这个文件最终会不会特别大?

不用担心,winston 支持按照大小自动分割文件:

我们指定 maxsize 为 1024 字节,也就是 1kb。

然后再跑几次:

大概跑了 10 次左右,出现了第二个文件:

而这时第一个日志文件刚好是 1kb:

这就是根据大小自动分割日志文件的功能。

有同学说,一般日志都是按照日期自动分割的,比如 2023-10-28 的日志文件,2023-10-29 的日志文件,这样之后也好管理。

这个支持么?

当然支持,但是要换别的 Transport 了。

winston 文档里可以看到有很多 Transport:

Console、File、Http、Stream 这几个 Transport 是内置的。

下面还有很多社区的 Transport,比如 MongoDB 的 Transport,很明显就是把日志写入 mongodb 的。

这里的 DailyRotateFile 就是按照日期滚动存储到日志文件的 Transport。

我们试试看:

css 复制代码
npm install --save winston-daily-rotate-file

安装这个 Transport。

然后改下代码:

javascript 复制代码
import winston from 'winston';
import 'winston-daily-rotate-file';

const logger = winston.createLogger({
    level: 'debug',
    format: winston.format.simple(),
    transports: [
        new winston.transports.Console(),
        new winston.transports.DailyRotateFile({
            level: 'info',
            dirname: 'log2',
            filename: 'test-%DATE%.log',
            datePattern: 'YYYY-MM-DD-HH-mm',
            maxSize: '1k'
        })
    ]
});

logger.info('光光光光光光光光光');
logger.error('东东东东东东东东');
logger.debug(66666666);

这里使用了 DailyRotateFile 的 transport,然后指定了文件名和日期格式。

指定文件名里的日志格式包含分钟,所以不同的分钟打印的日志会写入不同文件里:

这就达到了滚动日志的效果。

基本上,内置的和社区的 transport 就足够用了,不管是想把日志发送到别的服务,还是把日志存到数据库等,都可以用不同 Transport 实现。

再就是日志级别,winston 有 6 种级别的日志:

从上往下,重要程度依次降低。

比如当你指定 level 是 info 时,那 info、warn、error 的日志会输出,而 http、debug 这些不会。

日志级别的功能虽然简单,但却是很实用的功能。

日志可以通过 format 指定格式:

simple:

json:

prettyPrint(比 json 的格式多了一些空格):

用 combine 组合 timestamp 和 json:

或者再组合个 label:

加上个标签,再搜索相关日志就方便多了。

彩色:

通过这些,就可以指定各种日志格式。

但现在有个问题,如果我不同的 transport 要指定不同的格式呢?

可以这样:

javascript 复制代码
import winston from 'winston';

const logger = winston.createLogger({
    level: 'debug',
    transports: [
        new winston.transports.Console({
            format: winston.format.combine(
                winston.format.colorize(),
                winston.format.simple()
            ),
        }),
        new winston.transports.File({ 
            dirname: 'log3',
            filename: 'test.log',
            format: winston.format.json()
        }),
    ]
});

logger.info('光光光光光光光光光');
logger.error('东东东东东东东东');
logger.debug(66666666);

每个 transport 单独指定 format 就好了。

那如果我有的日志只想 console,而有的日志希望写入文件,而且配置都不同呢?

我们可以创建多个 logger 实例,每个 logger 实例有不同的 format、transport、level 等配置:

javascript 复制代码
import winston from 'winston';

winston.loggers.add('console', {
    format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
    ),
    transports: [
        new winston.transports.Console()
    ]
});

winston.loggers.add('file', {
    format:winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
    ),
    transports: [
        new winston.transports.File({
            dirname: 'log4',
            filename: 'test.log',
            format: winston.format.json()
        })
    ]
});


const logger1 = winston.loggers.get('console');

logger1.info('aaaaa');
logger1.error('bbbbb');

const logger2 = winston.loggers.get('file');

logger2.info('xxxx');
logger2.info('yyyy');

我们创建了 2 个 logger 实例,其中一个只写入 console,另一个只写入 file,并且 format 都不同。

然后分别用不同的 logger 来打印日志。

这样,项目中有不同的日志需求的时候,就可以创建多个 logger 实例。

总结

Node 服务端我们不会用 console.log 打印日志,而是会用日志框架,比如 winston。

winston 支持 tranport 配置,可以把日志传输到 console、file、通过 http 发送到别的服务,写入 mongodb 数据库等。

社区有很多 transport 可用,我们尝试了滚动日志的 transport,可以根据日期来自动分割日志文件。

winston 还支持 level 配置,可以根据级别来过滤日志。

而且还支持 format 的设置,比如 json、simple、label、timstamp 等,一般我们输出到文件里的都是 json 格式,并且给他加上时间戳和 label,这样方便之后分析。

每个 transport 都可以单独指定 format,而且还可以创建多个 logger,每个 logger 用不同的配置。

总之,相比直接 console.log,用 winston 这样的灵活强大的日志框架可太香了。

更多内容可以看我的小册《Nest 通关秘籍》

相关推荐
小徐_23336 小时前
wot-ui-cli 1.1.0 发布:支持 OpenCode、Antigravity,wot-ui 图标迁移不再靠猜
前端
__zRainy__6 小时前
Node.js Web 框架选型指南:从 Express 到 Hono 的全景对比
前端·node.js·express·koa·nestjs·egg·fastify
西瓜有点饿6 小时前
PostCSS 和 UnoCSS 的作用和区别
前端·postcss
mCell6 小时前
Lua 编程入门:从基础语法到元表
javascript·算法·lua
伟大的兔神7 小时前
我做了一个本地优先的 AI 图片工作台:Loomora v1.0.0 正式发布
前端·javascript·vue.js
90后的晨仔8 小时前
uni-app项目 Vue3 状态管理 Pinia 完全指南:从概念到实战的深度解析
前端
90后的晨仔8 小时前
uni-app 在 iOS 平台跳转页面时移除底部安全区域的完整技术指南
前端
用户938515635078 小时前
React + JWT 登录鉴权底层原理与工程化实践
前端·react.js
zww89491119 小时前
家政派单系统开发实战:架构设计与派单算法指南
前端·系统架构
fangzhanpeng1689 小时前
(前端)2.js变量作用域样例
开发语言·前端·javascript