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 configurationconst logConfiguration = { 'transports': [ new winston.transports.File({ filename: 'logs/example.log' }) ]};
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 messageslogger.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!");
这规定:
- 日志将显示在控制台输入中。
- 只有属于谬误级别的日志才会记录在 example.log 文件中。
应用 Winston,您能够指定保留日志的默认格局。
例如,假如咱们想以 JSON 格局登录,咱们能够应用 Winston.format 指定,并且日志实例将以 JSON 格局保留。
format: winston.format()
该格局采纳其余日志表单属性,例如
- 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 messagelogger.info("Hello, Winston logger, some info!");
输入:
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}`), )}),});
app.js:
const express = require('express');// Require logger.jsconst logger = require('./utils/logger');const app = express();const port = 3000;const host = "localhost";// Dummy Express GET callapp.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 errorsapp.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 erorsapp.use((req,res,next) => { res.status(404).send("PAGE NOT FOUND"); logger.error(`400 || ${res.statusMessage} - ${req.originalUrl} - ${req.method} - ${req.ip}`);})// Run the serverapp.listen(port, () => { console.log("Server started..."); logger.info(`Server started and running on http://${host}:${port}`)})
每次服务器启动时,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的原创文章,尽在:"汪子熙":