介绍
有效的日志解决方案对于任何应用程序的成功至关重要。在本指南中,我们将专注于一个名为 Winston的日志解决方案,一个极其多功能的日志解决方案和最受欢迎的日志解决方案,基于NPM下载统计。 温斯顿的功能包括支持多个存储选项和日志级别,日志查询,甚至是一个内置的个人资料库。 本教程将向您展示如何使用温斯顿登录一个Node/Express应用程序,我们将创建作为这个过程的一部分。 我们还将研究如何将温斯顿与另一个受欢迎的HTTP请求中间软件用于Nodejs的日志,称为 Morgan以将HTT
完成本教程后,您将有一个 Ubuntu 服务器运行一个小 Node/Express 应用程序. 您还将有 Winston 实现记录错误和消息到文件和控制台。
前提条件
在您开始本指南之前,您将需要以下内容:
- 一个 Ubuntu 16.04 服务器是按照 Ubuntu 16.04 初始服务器安装指南设置的,包括一个 sudo 非根用户和防火墙。
有了这些前提条件,我们可以构建我们的应用程序并安装Winston。
步骤 1 — 创建基本节点/Express 应用程序
温斯顿的常见用途是从 Node.js 构建的 Web 应用程序中记录事件。为了充分展示如何嵌入 Winston,我们将使用 Express 框架创建一个简单的 Node.js Web 应用程序。 为了帮助我们获得一个基本的 Web 应用程序运行,我们将使用 express-generator
,一个命令行工具,以便快速运行 Node/Express Web 应用程序。 因为我们安装了 Node Package Manager作为我们的先决条件的一部分,我们将能够使用 npm
命令来安装 express-generator
。 我们还将使用 -g
旗帜,该旗帜在全球范围内安装该包,因此它可以作为现有 Node 项目/模块外部的命
1sudo npm install express-generator -g
安装了快递生成器
,我们可以使用快递
命令创建我们的应用程序,然后是我们想要为我们的项目使用的目录名称。
1express myApp
接下来,安装 Nodemon,这将自动重新加载应用程序,当我们做任何更改时。Node.js应用程序需要重新启动任何时候对源代码进行更改,以便这些更改生效。Nodemon将自动监控更改并为我们重新启动应用程序。
1sudo npm install nodemon -g
要完成应用程序的设置,请更改应用程序目录并如下安装依赖:
1cd myApp
2npm install
默认情况下,使用快递生成器
创建的应用程序在端口3000上运行,所以我们需要确保端口不会被防火墙阻止。
1sudo ufw allow 3000
我们现在有我们需要启动我们的Web应用程序所需的一切。 要做到这一点,请运行以下命令:
1nodemon bin/www
这将启动在端口3000上运行的应用程序,我们可以通过在网页浏览器中访问http://your_server_ip:3000
来测试它是否正在运作。
对于本文的其余部分,我们将参考我们迄今使用的SSH会话,目前正在运行应用程序为Session A. 我们将使用新的SSH会话运行命令和编辑文件,我们将把这个会话称为Session B. 除非另有说明,所有剩余的命令应在Session B中运行。
步骤 2 - 定制 Node.js 应用程序
由快递生成器
创建的默认应用程序在让我们开始做得很好,甚至包括我们将使用的摩根HTTP日志中间软件来记录所有HTTP请求的数据。
默认情况下,快递发电机
锅炉板在引用摩根
包时使用变量 logger. 由于我们将使用摩根
和温斯顿
,这两个都是日志包,所以将其中一个称为 logger 可能会令人困惑,所以让我们通过编辑项目根中的app.js
文件并做出一些更改来改变这一点。
要打开app.js
进行编辑,请使用nano
命令:
1nano ~/myApp/app.js
在文件的顶部找到以下行:
1[label ~/myApp/app.js]
2...
3var logger = require('morgan');
4...
将其更改为如下:
1[label ~/myApp/app.js]
2...
3var morgan = require('morgan');
4...
我们还需要找到变量 logger在文件中被引用的地方,并将其更改为morgan
。当我们在其中时,让我们将morgan
包所使用的日志格式更改为combined
,这是标准的Apache日志格式,并将包括在日志中有用的信息,如远程IP地址和用户代理HTTP请求标题。
要做到这一点,找到下面的行:
1[label ~/myApp/app.js]
2...
3app.use(logger('dev'));
4...
将其更改为如下:
1[label ~/myApp/app.js]
2...
3app.use(morgan('combined'));
4...
这些变化将有助于我们更好地了解我们在整合我们的温斯顿配置后,我们在任何时候参考哪个日志包。
退出并保存文件,键入CTRL-X
,然后Y
,然后ENTER
。
现在,我们的应用已经设置,我们已经准备好开始与温斯顿合作。
步骤 3 – 安装和配置 Winston
在此步骤中,我们将探索作为Winston
包的一部分可用的某些配置选项,并创建一个日志器,将信息记录到文件和控制台。
要安装Winston
,运行以下命令:
1cd ~/myApp
2npm install winston
对于我们的应用程序,将任何类型的支持或实用程序配置文件保存在一个特殊目录中,所以让我们创建一个包含winston
配置的config
文件夹:
1mkdir ~/myApp/config
现在让我们创建包含我们的winston
配置的文件,我们将称之为winston.js
:
1touch ~/myApp/config/winston.js
接下来,创建一个包含您的日志文件的文件夹:
1mkdir ~/myApp/logs
最後,讓我們安裝「app-root-path」,這是一個在指定 Node.js 中的路徑時有用的套件,這個套件並不直接與 Winston 相關,但在指定 Node.js 代碼中的檔案路徑時非常有幫助。
1npm install app-root-path --save
我们需要配置如何处理我们的日志,所以我们可以继续定义我们的配置设置。 开始打开 `~/myApp/config/winston.js’ 来编辑:
1nano ~/myApp/config/winston.js
接下来,需要app-root-path
和winston
包:
1[label ~/myApp/config/winston.js]
2var appRoot = require('app-root-path');
3var winston = require('winston');
有了这些变量,我们可以为我们的 transports 定义配置设置。 运输是温斯顿引入的概念,指用于日志的存储/输出机制。 温斯顿提供三个核心运输 - console, file 和 HTTP. 我们将专注于本教程的控制台和文件运输:控制台运输将记录信息到控制台,文件运输将记录信息到指定文件。 每个运输定义可以包含自己的配置设置,如文件大小,日志级别和日志格式。
- 水平 - 要记录的消息水平.
- filename - 用于写日志数据的文件.
- handleExceptions - 捕捉和日志未处理的例外.
- json - 记录日志数据在JSON格式.
- maxsize - 日志文件的最大尺寸,在字节中,在创建新文件之前。
- maxFiles - 限制在日志文件大小超过时创建的文件数量。
_Logging 级别表示消息优先级,并以整数表示。Winston使用从 0 到 5 (最高到最低) 的npm
日志级别:
- 0: error
- 1: warn
- 2: info
- 3: verbose
- 4: debug
- 5: silly
指定特定运输的日志级别时,会记录该级别或更高级别的任何东西。例如,通过指定info
级别,会记录error
,warn
或info
级别的任何东西。
我们可以在温斯顿
配置中定义文件
和控制台
传输的配置设置如下:
1[label ~/myApp/config/winston.js]
2...
3var options = {
4 file: {
5 level: 'info',
6 filename: `${appRoot}/logs/app.log`,
7 handleExceptions: true,
8 json: true,
9 maxsize: 5242880, // 5MB
10 maxFiles: 5,
11 colorize: false,
12 },
13 console: {
14 level: 'debug',
15 handleExceptions: true,
16 json: false,
17 colorize: true,
18 },
19};
接下来,使用选项
变量中定义的属性来实时化一个新的温斯顿
日志记录器与文件和控制台传输:
1[label ~/myApp/config/winston.js]
2...
3var logger = new winston.Logger({
4 transports: [
5 new winston.transports.File(options.file),
6 new winston.transports.Console(options.console)
7 ],
8 exitOnError: false, // do not exit on handled exceptions
9});
默认情况下,morgan
只输出到控制台,所以让我们定义一个流函数,可以将morgan
生成的输出输入到winston
日志文件中。
1[label ~/myApp/config/winston.js]
2...
3logger.stream = {
4 write: function(message, encoding) {
5 logger.info(message);
6 },
7};
最后,导出日志器,以便在应用程序的其他部分中使用:
1[label ~/myApp/config/winston.js]
2...
3module.exports = logger;
完成的Winston
配置文件应该是这样的:
1[label ~/myApp/config/winston.js]
2var appRoot = require('app-root-path');
3var winston = require('winston');
4
5// define the custom settings for each transport (file, console)
6var options = {
7 file: {
8 level: 'info',
9 filename: `${appRoot}/logs/app.log`,
10 handleExceptions: true,
11 json: true,
12 maxsize: 5242880, // 5MB
13 maxFiles: 5,
14 colorize: false,
15 },
16 console: {
17 level: 'debug',
18 handleExceptions: true,
19 json: false,
20 colorize: true,
21 },
22};
23
24// instantiate a new Winston Logger with the settings defined above
25var logger = new winston.Logger({
26 transports: [
27 new winston.transports.File(options.file),
28 new winston.transports.Console(options.console)
29 ],
30 exitOnError: false, // do not exit on handled exceptions
31});
32
33// create a stream object with a 'write' function that will be used by `morgan`
34logger.stream = {
35 write: function(message, encoding) {
36 // use the 'info' log level so the output will be picked up by both transports (file and console)
37 logger.info(message);
38 },
39};
40
41module.exports = logger;
退出并保存文件。
我们现在已经配置了我们的日志器,但我们的应用程序仍然不知道它或如何使用它,我们现在将将日志器与应用程序集成。
步骤 4 – 与我们的应用程序集成 Winston
要让我们的日志器与应用程序工作,我们需要让express
意识到它. 我们已经在步骤2中看到我们的express
配置位于app.js
,所以让我们将我们的日志器导入到这个文件中。
1nano ~/myApp/app.js
导入winston
在文件的顶部,其他需要声明:
1[label ~/myApp/app.js]
2...
3var winston = require('./config/winston');
4...
我们将使用流
选项,并将其设置为我们作为温斯顿
配置的一部分创建的流界面。
1[label ~/myApp/app.js]
2...
3app.use(morgan('combined'));
4...
把它改成这个:
1[label ~/myApp/app.js]
2...
3app.use(morgan('combined', { stream: winston.stream }));
4...
退出并保存文件。
我们已经准备好看到一些日志数据了!如果你在网页浏览器中重新加载页面,你应该在SSH Session A的控制台中看到类似的内容:
1[secondary_label Output]
2[nodemon] restarting due to changes...
3[nodemon] starting `node bin/www`
4info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
5
6info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
这里有两个日志条目 - 第一条用于向HTML页面请求,第二条用于附带的风格表. 由于每个传输都配置为处理信息
级别日志数据,我们还应该在位于~/myApp/logs/app.log
的文件传输中看到类似的信息。 但是,文件传输中的输出应该被写成JSON对象,因为我们在文件传输配置中指定了json: true
。 您可以在我们的JSON教程中了解更多有关JSON的信息(https://andsky.com/tech/tutorials/an-introduction-to-json)。 若要查看日志文件的内容,请运行以下命令:
1tail ~/myApp/logs/app.log
你应该看到类似于以下的东西:
1[environment second]
2{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] \"GET / HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\"\n","timestamp":"2018-03-07T17:29:36.962Z"}
3{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] \"GET /stylesheets/style.css HTTP/1.1\" 304 - \"http://167.99.4.120:3000/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\"\n","timestamp":"2018-03-07T17:29:37.067Z"}
到目前为止,我们的日志器只记录 HTTP 请求和相关数据. 这是我们日志中必须有非常重要的信息,但我们如何记录自定义日志消息? 肯定会有一些时候我们想要这种能力,例如记录错误或配置数据库查询性能。
快递生成器
包默认包含一个404和500错误处理路线,所以我们将与之合作。
1nano ~/myApp/app.js
查找文件底部的代码块,它看起来像这样:
1[label ~/myApp/app.js]
2...
3// error handler
4app.use(function(err, req, res, next) {
5 // set locals, only providing error in development
6 res.locals.message = err.message;
7 res.locals.error = req.app.get('env') === 'development' ? err : {};
8
9 // render the error page
10 res.status(err.status || 500);
11 res.render('error');
12});
13...
这是最后的错误处理路径,最终会将错误响应发送回客户端. 由于所有服务器侧错误都会通过这个路径运行,这是一个包含温斯顿
日志器的好地方。
由于我们现在正在处理错误,我们想要使用错误
日志级别。再次,两种传输都配置为记录错误
级别的消息,所以我们应该在控制台和文件日志中看到输出。
- err.status - HTTP 错误状态代码.如果没有,默认值为 500.
- err.message - 错误的细节.
- req.originalUrl - 被请求的 URL.
- req.path - 请求 URL 的路径部分.
- req.method - 请求的 HTTP 方法 (GET, POST, PUT 等)。
更新错误处理路径以匹配以下内容:
1[label ~/myApp/app.js]
2...
3// error handler
4app.use(function(err, req, res, next) {
5 // set locals, only providing error in development
6 res.locals.message = err.message;
7 res.locals.error = req.app.get('env') === 'development' ? err : {};
8
9 // add this line to include winston logging
10 winston.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
11
12 // render the error page
13 res.status(err.status || 500);
14 res.render('error');
15});
16...
退出并保存文件。
要测试这一点,让我们尝试访问一个在我们的项目中不存在的页面,这将引发一个404错误。 回到您的网页浏览器中,尝试加载以下URL: http://your_server_ip:3000/foo
. 应用程序已经设置为响应这样的错误,由于由 express-generator
创建的锅炉板。 您的浏览器应该显示一个错误消息,看起来像这样(您的错误消息可能比显示的更详细):
现在再看看SSH Session A中的控制台,错误应该有一个日志条目,而且由于颜色设置,它应该很容易找到。
1[secondary_label Output]
2[nodemon] starting `node bin/www`
3error: 404 - Not Found - /foo - GET - ::ffff:72.80.124.207
4info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:40:11 +0000] "GET /foo HTTP/1.1" 404 985 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
5
6info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:40:11 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/foo" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
至于文件日志器,运行尾巴
命令再次应该向我们显示新的日志记录:
1tail ~/myApp/logs/app.log
您将看到如下这样的消息:
1[environment second]
2{"level":"error","message":"404 - Not Found - /foo - GET - ::ffff:72.80.124.207","timestamp":"2018-03-07T17:40:10.622Z"}
错误消息包括我们专门指示Winston
作为错误处理器的一部分登录的所有数据,包括错误状态(404 - 未找到),请求的URL(本地主机/foo),请求方法(GET),请求的IP地址,以及请求时刻印。
结论
在本教程中,您建立了一个简单的 Node.js 网页应用程序,并集成了一个 Winston 日志解决方案,该解决方案将作为一个有效的工具,以提供对应用程序的性能的见解。