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

Node 里怎么打印日志呢?

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

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

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

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

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

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

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

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

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

我们试试看:

mkdir winston-test
cd winston-test
npm init -y

先创建个项目。

安装 winston:

npm install --save winston

然后写下 index.js

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。

我们试试看:

npm install --save winston-daily-rotate-file

安装这个 Transport。

然后改下代码:

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 要指定不同的格式呢?

可以这样:

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 等配置:

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 这样的灵活强大的日志框架可太香了。

相关推荐

  • offer 选择难?说说我的 2 个思考
  • 盘点JS中数组去重写法
  • 逃离国企,我好快乐!
  • Yarn 4.0正式发布,现代化的软件包管理器
  • 3202年了,为啥SSR并没有预想中的流行?
  • 大模型如何开启输入法的“iPhone时刻”?对话讯飞输入法总经理程坤
  • TF线下活动报名 | 11月4日,TF121邀您一起寻找企业数字化的第二曲线!
  • 最好7B模型再易主!打败700亿LLaMA2,苹果电脑就能跑|开源免费
  • 姚期智Hinton Bengio联名发文:18个月内AI规模将扩大100倍,得有人管管了
  • 「20万级最强智驾」还不用激光雷达,极越01到底是一款什么样的车?
  • 正面硬刚OpenAI!智谱AI推出第三代基座模型,功能对标GPT-4V,代码解释器随便玩
  • 谷歌 20 亿美元投资 AI 初创公司 Anthropic,AI 赛道竞赛再加速
  • 2秒出图的文生图模型出现了!清华提出LCM,新一代图像生成里程碑
  • 给你的 SpringBoot 工程部署的 jar 包瘦瘦身吧!
  • 【进阶玩法】策略+责任链+组合实现合同签章
  • 一个Demo搞定前后端大文件分片上传、断点续传、秒传
  • 重磅!西工大李学龙团队研发大模型自主无人机集群!
  • 8年干出千亿市值,东南亚最大快递IPO了
  • 李克强:大力发展新一代信息技术、人工智能、数字经济等(2020 年 1 月)
  • 再看大模型微调数据质量如何评估:已有方法回顾及IFD指令遵循难度筛选的思路与聚类细节