关键词搜索

源码搜索 ×
×

关于 Node.js 应用里使用 winston 进行日志记录的最佳实践

发布2021-10-22浏览1106次

详情内容

Logging with Winston and Node.js

假设您有一个在生产环境中运行的应用程序,每天有数百万用户赚取数千美元。应用程序可能存在错误的原因有多种,作为开发人员,您需要找出原因并修复它。没有人愿意使用有问题的应用程序,修复错误会花费时间和金钱。

你怎么能解决这个问题?也许通过回到代码并检查每一行代码是否按预期运行。这对于小型应用程序来说更容易,但即便如此,尝试触发与用户相同类型的错误也可能很困难。想象一下,这在大型应用程序中会有多难。

假设有一个实例,应用程序收集一些用户的信息并将它们保存到数据库中。

如果应用程序失败,服务器将向最终用户返回系统错误。最好能捕获这些实例并解决这些错误。在这种情况下,您如何知道用户 a 或用户 b 遇到了单个系统错误?这些错误可能由代码中的错误、损坏的文件、错误的逻辑或数据类型不匹配触发。

如果你需要避免这种挫折,你就无法避免日志记录。日志是程序员首先要查找的地方,用于跟踪错误和事件流,尤其是来自服务器的事件。日志会告诉您当应用程序运行并与用户交互时会发生什么。日志记录的一个很好的用例是,例如,如果您的系统中有一个错误,并且您想了解导致其发生的步骤。

日志记录是将应用程序活动生成的信息记录到日志文件中的过程。保存在日志文件中的消息称为日志。日志是记录在日志文件中的单个实例。

Node.js 中构建应用程序日志至关重要。在本地运行应用程序时,可以将其挂接到调试器上,非常棒,可以在运行应用程序时发现问题。在开发过程中,您通常会使用 console.log 来获取应用程序日志。

但是当一个应用程序投入生产并且用户开始与之交互时,你就不能再使用 console.log 了。如果出现问题并且应用程序崩溃,则无法使用控制台进行检查。如果你有一个简洁、干净和高质量的日志中间件,比如 Winston,那会很有帮助。

Winston 处理您的应用程序活动并将有用的信息生成到日志文件或数据库中。之后,您可以检查应用程序生成的所有活动。

本指南将在 Winston 的上下文中解释日志记录。

了解生产应用程序是否出现问题的唯一方法是创建日志。 记录重新创建并为您保存该问题。 如果出现问题或出现问题,日志会告诉您。

了解系统的行为方式。 日志记录将生成有关系统如何与用户交互以及进出系统的信息。

跟踪您的系统活动。 日志可以显示实例发生的时间以及触发日志的原因。

通常,日志记录的临界值是:

  • 错误跟踪
  • 调试
  • 应用性能

选择 winston 的收益

Winston 是最好的日志中间件之一,每周下载量约为 4,000,000 次。以下属性使 Winston 成为整体通用的日志记录中间件。

  • 它使用简单且可配置。
  • 日志级别(优先级)。 Winston 提供日志记录级别。它们表示日志优先级;这使您能够从需要较少关注的日志中整理出关键日志。例如:{错误:0,警告:1,信息:2,详细:3,调试:4,傻:5}。在这种情况下,错误日志的优先级高于详细日志。
  • 记录通道(传输)。一个好的记录器有不同的方式来选择你的日志输出目的地。使用 Winston,您可以以不同方式发送和保存日志,例如文件、数据库、电子邮件和控制台。
  • 日志格式。 Winston 为您提供了多种日志格式。例如,在将日志保存到 Mongo 数据库时,日志格式需要为 JSON 格式。
  • 日志分析。 Winston 可帮助您分析代码块并测量成功执行代码所需的时间。

Winston transporters

Winston 的特性之一是它支持各种传输,例如文件传输。 这会将生成的日志消息保存到日志文件中。 该文件是在您的系统中指定的。 如果应用程序创建了它的第一个日志实例,该文件将自动生成。 之后,任何日志都将保存到创建的文件中。

为此,记录器配置对象需要指向一个文件(文件传输器)。 只需将新 winston.transports.File 中的传输配置对象 .transports.Console() 替换为 .transports.File() 即可,如下所示。

transports.File({
    filename: 'logs/example.log'
})

    这指定生成的任何日志都将保存在日志文件夹下的 example.log 文件中。

    传输配置将是:

    // Logger configuration
    const logConfiguration = {
        'transports': [
            new winston.transports.File({
                filename: 'logs/example.log'
            })
        ]
    };
    
      4
    • 5
    • 6
    • 7
    • 8

    log 文件的内容:

    Winston 允许您实现多个日志传输,即可以将日志记录到文件、控制台或数据库中。

    下面的 Logger 配置记录到控制台和文件。 我们将向日志配置对象添加一个传输数组。 在本指南的后面,我们将向您展示如何将日志实例记录到数据库中。

    const logConfiguration = {
        transports: [
            new winston.transports.Console({
                level: 'warn'
            }),
            new winston.transports.File({
                level: 'error',
                // Create the log directory if it does not exist
                filename: 'logs/example.log'
            })
        ]
    };
    
    const logger = winston.createLogger(logConfiguration);
    
    // Log some messages
    logger.error("Hello, Winston logger, the first error!");
    logger.warn("Hello, Winston logger, the first warning!");
    logger.warn("Hello, Winston logger, the second warning!");
    logger.error("Hello, Winston logger, the second error!");
    logger.info("Hello, Winston logger, some info!");
    logger.debug("Hello, Winston logger, a debug!");
    
      4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这规定:

    • 日志将显示在控制台输出中。
    • 只有属于错误级别的日志才会记录在 example.log 文件中。

    使用 Winston,您可以指定保存日志的默认格式。

    例如,假设我们想以 JSON 格式登录,我们可以使用 Winston.format 指定,并且日志实例将以 JSON 格式保存。

    format: winston.format(
    )
    
    • 1
    • 2

    该格式采用其他日志表单属性,例如

    • ms() - 自记录上次日志以来的时间(以毫秒为单位)。
    • label() - 向记录的消息添加标签。
    • timestamp() - 收到日志消息的时间戳。
    • splat() - 提供字符串插值。

    要将其应用到您的日志中,您需要使用 format.combine,如下例所示。

    const logConfiguration = {
        transports: [
            new winston.transports.Console()
        ],
        format: winston.format.combine(
            winston.format.label({
                label: `Label?️`
            }),
            winston.format.timestamp({
               format: 'MMM-DD-YYYY HH:mm:ss'
           }),
            winston.format.printf(info => `${info.level}: ${info.label}: ${[info.timestamp]}: ${info.message}`),
        )
    };
    
    const logger = winston.createLogger(logConfiguration);
    
    // Log a message
    logger.info("Hello, Winston logger, some info!");
    
      4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    输出:

    info: Label?️: Nov-18-2020 08:10:44: Hello, Winston logger, some info!

    Configuring Winston with a server

    让我们创建一个简单的 Express 服务器,我们可以使用 Winston 进行一些日志记录。 这将是一个小项目,可以让您使用 Winston 记录来自服务器请求和响应的日志。

    继续使用 npm install express 安装 Express 库。

    这是我们小项目的结构:

    • Logs - 将保存由 Winston 文件传输生成的日志文件。
    • app.js - 将成为我们的服务器应用程序。
    • Utils - 将保存 Winston logger.js,我们将在其中添加 Winston 传输和格式等配置。

    Logger.js:

    const { createLogger, format, transports } = require('winston');
    
    module.exports = createLogger({
    transports:
        new transports.File({
        filename: 'logs/server.log',
        format:format.combine(
            format.timestamp({format: 'MMM-DD-YYYY HH:mm:ss'}),
            format.align(),
            format.printf(info => `${info.level}: ${[info.timestamp]}: ${info.message}`),
        )}),
    });
    
      4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    app.js:

    const express = require('express');
    
    // Require logger.js
    const logger = require('./utils/logger');
    const app = express();
    const port = 3000;
    const host = "localhost";
    
    // Dummy Express GET call
    app.get('/',(req,res) => {
        res.send("Hello World!");
        logger.info("Server Sent A Hello World!");
    })
    
    // Introduce error by using undefined variable 'y'
    app.get('/calc',(req,res) => {
        const x = y + 10;
        res.send(x.toString());
    })
    
    // Capture 500 errors
    app.use((err,req,res,next) => {
    res.status(500).send('Could not perform the calculation!');
       logger.error(`${err.status || 500} - ${res.statusMessage} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
    })
    
    // Capture 404 erors
    app.use((req,res,next) => {
        res.status(404).send("PAGE NOT FOUND");
        logger.error(`400 || ${res.statusMessage} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
    })
    // Run the server
    app.listen(port, () => {
        console.log("Server started...");
        logger.info(`Server started and running on http://${host}:${port}`)
    })
    
      4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    每次服务器启动时,Winston 都会将日志记录到 server.log 文件中。

    服务器运行时,访问以下页面会在每次调用链接时创建日志。

    http://localhost:3000/ - 服务器将发送一条 hello world 消息。 我们希望 Winston 捕获它并将其记录在我们的日志文件中。

    http://localhost:3000/calc - 我们试图将变量 y 添加到变量 x。 在这种情况下,未定义变量 y。 这将产生一个错误,我们希望 Winston 为我们捕获这个实例。

    http://localhost:3000/hello - 我们创建的服务器没有这样的 URL。 我们希望 Winston 在指向我们 IP 地址的链接被访问但无法找到时通知我们; 那是 404 错误。

    output:

    info: Nov-12-2020 10:07:59: Server started and running on http://localhost:3000
    info: Nov-12-2020 10:08:02: Server Sent A Hello World!
    error: Nov-12-2020 10:08:05: 500 - Internal Server Error - y is not defined - /calc - GET - ::1
    error: Nov-12-2020 10:08:10: 400 || Not Found - /hello - GET - ::1

    更多Jerry的原创文章,尽在:“汪子熙”:

    相关技术文章

    点击QQ咨询
    开通会员
    返回顶部
    ×
    微信扫码支付
    微信扫码支付
    确定支付下载
    请使用微信描二维码支付
    ×

    提示信息

    ×

    选择支付方式

    • 微信支付
    • 支付宝付款
    确定支付下载