如何使用Winston记录Ubuntu 20.04上的Node.js应用程序

介紹

一個有效的日誌記錄解決方案對於任何應用程序的成功至關重要。Winston是一個功能強大的日誌記錄庫,也是Node.js應用程序中可用的流行日誌記錄解決方案之一。Winston的功能包括支持多種存儲選項、日誌級別、日誌查詢和內置分析器。

在本教程中,您將使用Winston來記錄一個您將作為本過程的一部分創建的Node/Express應用程序。您還將看到如何將Winston與Morgan結合使用,Morgan是另一個Node.js中流行的HTTP請求中間件記錄器,以將HTTP請求數據日誌與其他信息統合起來。完成本教程後,您的Ubuntu服務器將運行一個小型的Node/Express應用程序,並且Winston將被實現以將錯誤和消息記錄到文件和控制台。

先決條件

要按照本教程,您需要:

步驟 1 — 創建基本的 Node/Express 應用程式

Winston 經常用於記錄由使用 Express 框架構建的 Web 應用程式中的事件。在此步驟中,您將使用 Express 框架創建一個簡單的 Node.js Web 應用程式。您將使用express-generator,一個命令行工具,快速啟動一個 Node/Express Web 應用程式。

由於在先決條件中安裝了Node 套件管理器,您可以使用npm命令安裝express-generator

  1. sudo npm install express-generator -g

-g 標誌會全局安裝套件,這意味著它可以在現有的 Node 專案/模組之外用作命令行工具。

安裝了 express-generator 後,您可以使用 express 命令創建您的應用程式,然後是要用於該專案的目錄名稱:

  1. express myApp

在本教程中,該專案將被命名為 myApp

注意:也可以直接運行 express-generator 工具,而無需首先全局安裝為系統范圍的命令。要這樣做,運行以下命令:

  1. npx express-generator myApp

npx 命令是隨 Node Package Manager 一起提供的命令運行器,它使從 npm 登錄中運行命令行工具變得容易。

在第一次運行時,它會詢問您是否同意下載該套件:

Output
Need to install the following packages:
  express-generator
Ok to proceed? (y)

回答 y,然後按 ENTER。現在您可以使用 npx express-generator 代替 express

接下來,安裝 Nodemon,它將在您進行更改時自動重新加載應用程式。Node.js 應用程式需要在對源代碼進行更改時重新啟動,以便這些更改生效,因此 Nodemon 將自動監視更改並重新啟動應用程式。由於您希望能夠將 nodemon 用作命令行工具,請使用 -g 標誌安裝它:

  1. sudo npm install nodemon -g

要完成應用程式的設置,請移至應用程式目錄並安裝依賴項如下:

  1. cd myApp
  2. npm install

默認情況下,使用 express-generator 創建的應用程序運行在端口 3000,因此您需要確保防火牆不會阻止該端口。

要打開端口 3000,請運行以下命令:

  1. sudo ufw allow 3000

現在您擁有啟動 Web 應用程序所需的一切。要這樣做,請運行以下命令:

  1. nodemon bin/www

該命令將應用程序啟動在端口 3000 上。您可以通過將瀏覽器指向 http://your_server_ip:3000 來測試它是否工作。您應該會看到類似以下的內容:

在此時,您可以開始第二個 SSH 會話到您的服務器,用於本教程的其餘部分,將剛剛啟動的 Web 應用程序保持在原始會話中運行。本文的其餘部分中,當前運行應用程序的初始 SSH 會話將稱為 Session A。 Session A 中的任何命令將以深藍色背景顯示,如下所示:

  1. nodemon bin/www

您將使用新的 SSH 會話來運行命令和編輯文件。此會話將稱為 Session B。 Session B 中的任何命令將以淺藍色背景顯示,如下所示:

  1. cd ~/myApp

除非另有說明,否則您將在 Session B 中運行所有剩餘的命令。

在此步驟中,您已創建了基本應用程序。接下來,您將對其進行自定義。

步驟 2 — 自定義日誌變量

儘管由 express-generator 創建的默認應用程序是一個良好的開始,但您需要自定義應用程序以在需要時調用正確的記錄器。

express-generator 包含 Morgan HTTP 記錄中間件,您將使用它來記錄所有 HTTP 請求的數據。由於 Morgan 支持輸出流,它與 Winston 內置的流支持非常配合,使您可以將 HTTP 請求數據日誌與其他任何您選擇使用 Winston 日誌記錄的內容進行整合。

express-generator 的樣板在引用 morgan 套件時使用了變數 logger。由於您將使用 morganwinston,這兩個都是日誌記錄套件,將它們中的任何一個稱為 logger 可能會令人困惑。為了指定您想要的變量,您可以通過編輯 app.js 文件來更改變量聲明。

要打開 app.js 進行編輯,請使用 nano 或您喜歡的文本編輯器:

  1. nano ~/myApp/app.js

在文件頂部附近找到以下行:

~/myApp/app.js
...
var logger = require('morgan');
...

將變量名從 logger 更改為 morgan

~/myApp/app.js
...
var morgan = require('morgan');
...

此更新指定聲明的變量 morgan 將調用與 Morgan 請求記錄器相關聯的 require() 方法。

你需要找出文件中其他地方引用了变量logger,并将其更改为morgan。您还需要更改morgan包使用的日志格式为combined,这是标准的Apache日志格式,将在日志中包含有用的信息,例如远程IP地址和用户代理HTTP请求标头。

为此,请找到以下行:

~/myApp/app.js
...
app.use(logger('dev'));
...

将其更新为以下内容:

~/myApp/app.js
...
app.use(morgan('combined'));
...

这些更改将帮助您在集成Winston配置后了解在任何给定时间引用的是哪个日志记录包。

完成后,保存并关闭文件。

现在,您的应用程序已设置好,可以开始使用Winston。

步骤3 — 安装和配置Winston

在这一步中,您将安装和配置Winston。您还将探索作为winston包的一部分提供的配置选项,并创建一个记录器,将信息记录到文件和控制台。

使用以下命令安装winston

  1. cd ~/myApp
  2. npm install winston

将应用程序的任何支持或实用程序配置文件保存在一个特殊的目录中很有帮助。创建一个config文件夹,其中将包含winston配置:

  1. mkdir ~/myApp/config

接下來,創建一個包含日誌文件的文件夾:

  1. mkdir ~/myApp/logs

最後,安裝app-root-path

  1. npm install app-root-path --save

app-root-path套件在指定Node.js路徑時很有用。雖然這個套件與Winston沒有直接關係,但在確定Node.js中文件的路徑並避免醜陋的相對路徑語法時很有用。

現在,日誌處理的配置已經就位,您可以定義您的設置了。創建並打開~/myApp/config/winston.js進行編輯:

  1. nano ~/myApp/config/winston.js

winston.js文件將包含您的winston配置。

接下來,添加以下代碼以引入app-root-pathwinston套件:

~/myApp/config/winston.js
const appRoot = require('app-root-path');
const winston = require('winston');

有了這些變量,您可以為您的轉換定義配置設置。轉換是Winston引入的一個概念,它指的是用於日誌的存儲/輸出機制。Winston內置了四個核心轉換:ConsoleFileHTTPStream

本教程將專注於控制台和文件轉換。控制台轉換將信息記錄到控制台,而文件轉換將信息記錄到指定的文件中。每個轉換定義都可以包含配置設置,如文件大小、日誌級別和日誌格式。

以下是您將為每個轉換使用的設置的快速摘要:

  • level:日誌消息的級別。
  • filename:要用來寫入日誌數據的文件。
  • handleExceptions:捕獲並記錄未處理的異常。
  • maxsize:日誌文件的最大大小,以字節為單位,在創建新文件之前。
  • maxFiles:當日誌文件大小超出時創建的文件數量上限。
  • format:日誌輸出的格式。

日誌級別表示消息優先級,並以整數表示。 Winston 使用從 0 到 6(優先級從高到低)的 npm 日誌級別:

  • 0:error
  • 1:warn
  • 2:info
  • 3:http
  • 4:verbose
  • 5:debug
  • 6:silly

當為特定傳輸設置日誌級別時,將記錄該級別或更高級別的消息。例如,當設置級別為 info 時,將記錄 errorwarninfo 級別的消息。

在調用日誌記錄器時指定日誌級別,這意味著您可以運行以下命令來記錄錯誤:logger.error('test error message')

仍在配置文件中,添加以下代碼以定義 winston 配置中 fileconsole 傳輸的配置設置:

~/myApp/config/winston.js
...
// 定義每個傳輸方式(文件、控制台)的自定義設置
const options = {
  file: {
    level: "info",
    filename: `${appRoot}/logs/app.log`,
    handleExceptions: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5,
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    ),
  },
  console: {
    level: "debug",
    handleExceptions: true,
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  },
};

接下來,將以下代碼添加到使用options變量中定義的屬性來實例化一個新的winston記錄器,並使用文件和控制台傳輸:

~/myApp/config/winston.js
...
// 使用上面定義的設置實例化一個新的Winston Logger
const logger = winston.createLogger({
  transports: [
    new winston.transports.File(options.file),
    new winston.transports.Console(options.console),
  ],
  exitOnError: false, // 不要在處理的異常中退出
});

默認情況下,morgan僅將輸出到控制台,因此您將定義一個流函數,該函數將能夠將morgan生成的輸出輸入到winston日誌文件中。您將使用info級別來捕獲兩種傳輸(文件和控制台)的輸出。將以下代碼添加到配置文件中:

~/myApp/config/winston.js
...
// 創建一個具有'write'功能的流對象,該功能將被`morgan`使用
logger.stream = {
  write: function(message, encoding) {
    // 使用'info'日誌級別,以便輸出將被兩種
    // 傳輸(文件和控制台)捕獲
    logger.info(message);
  },
};

最後,添加下面的代碼以導出記錄器,以便在應用程序的其他部分中使用:

~/myApp/config/winston.js
...
module.exports = logger;

完成的winston配置文件現在看起來像這樣:

~/myApp/config/winston.js
const appRoot = require("app-root-path");
const winston = require("winston");

// 定義每個傳輸(文件、控制台)的自定義設置
const options = {
  file: {
    level: "info",
    filename: `${appRoot}/logs/app.log`,
    handleExceptions: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5,
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    ),
  },
  console: {
    level: "debug",
    handleExceptions: true,
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  },
};

// 使用上面定義的設置實例化一個新的 Winston Logger
const logger = winston.createLogger({
  transports: [
    new winston.transports.File(options.file),
    new winston.transports.Console(options.console),
  ],
  exitOnError: false, // 不要在處理過的異常上退出
});

// 創建一個具有 'write' 函數的流對象,該函數將被 `morgan` 使用
logger.stream = {
  write: function (message, encoding) {
    // 使用 'info' 日誌級別,以便輸出將被兩個傳輸(文件和控制台)接收
    // 傳輸(文件和控制台)
    logger.info(message);
  },
};

module.exports = logger;

保存並關閉文件。

現在您已經配置了記錄器,但是您的應用程序仍然不知道它,也不知道如何使用它,因此您需要將記錄器與應用程序集成。

步驟 4 — 將 Winston 集成到應用程序中

為了讓您的記錄器與應用程序一起工作,您需要讓 express 知道它。您在第 2 步中看到您的 express 配置位於 app.js 中,因此您可以將您的記錄器導入該文件。

打開文件進行編輯:

  1. nano ~/myApp/app.js

在文件頂部與其他 require 聲明附近添加一個 winston 變量聲明:

~/myApp/app.js
...
var winston = require('./config/winston');
...

你将首次使用winston是在morgan中。仍然在app.js中,找到以下行:

~/myApp/app.js
...
app.use(morgan('combined'));
...

更新它以包括stream选项:

~/myApp/app.js
...
app.use(morgan('combined', { stream: winston.stream }));
...

在这里,你将stream选项设置为你在winston配置中创建的流接口。

保存并关闭文件。

在这一步中,你配置了Express应用程序以与Winston一起使用。接下来,你将查看日志数据。

第5步 — 访问日志数据并记录自定义日志消息

现在应用程序已配置好,你可以查看一些日志数据了。在这一步中,你将查看日志条目并使用示例自定义日志消息更新你的设置。

如果你在Web浏览器中重新加载页面,你应该在SSH会话A的控制台中看到类似以下输出:

Output
[nodemon] restarting due to changes... [nodemon] starting `node bin/www` info: ::1 - - [25/Apr/2022:18:10:55 +0000] "GET / HTTP/1.1" 200 170 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36" info: ::1 - - [25/Apr/2022:18:10:55 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"

这里有两个日志条目:第一个是请求HTML页面的日志;第二个是相关的样式表。由于每个传输都配置为处理info级别的日志数据,你还应该在~/myApp/logs/app.log位置的文件传输中看到类似的信息。

要查看日志文件的内容,请运行以下命令:

  1. tail ~/myApp/logs/app.log

tail會在您的終端中輸出文件的最後部分。

您應該會看到類似以下的內容:

{"level":"info","message":"::1 - - [25/Apr/2022:18:10:55 +0000] \"GET / HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\"\n","timestamp":"2022-04-25T18:10:55.573Z"}
{"level":"info","message":"::1 - - [25/Apr/2022:18:10:55 +0000] \"GET /stylesheets/style.css HTTP/1.1\" 304 - \"http://localhost:3000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\"\n","timestamp":"2022-04-25T18:10:55.588Z"}

文件傳輸中的輸出將被寫入一個JSON對象,因為您在文件傳輸配置的format選項中使用了winston.format.json()。您可以在JSON介紹中了解更多關於JSON的信息。

到目前為止,您的日誌記錄器只記錄了HTTP請求和相關數據。這些信息在日誌中是必不可少的。

在將來,您可能會想要記錄自定義的日誌消息,例如記錄錯誤或分析數據庫查詢性能。作為示例,您將從錯誤處理程序路由調用日誌記錄器。默認情況下,express-generator套件已經包含了404500錯誤處理程序路由,因此您將使用它。

打開~/myApp/app.js文件:

  1. nano ~/myApp/app.js

找到文件底部看起來像這樣的代碼塊:

~/myApp/app.js
...
// 錯誤處理程序
app.use(function(err, req, res, next) {
  // 只在開發中提供錯誤
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // 渲染錯誤頁面
  res.status(err.status || 500);
  res.render('error');
});
...

這一部分是最終的錯誤處理路由,最終將錯誤響應發送回客戶端。由於所有服務器端錯誤都會通過此路由運行,所以在這個地方包含winston記錄器是一個不錯的選擇。

因為你現在正在處理錯誤,你想使用 error 日誌級別。兩個傳輸設置都配置為記錄 error 級別的消息,所以你應該能在控制台和文件日誌中看到輸出。

你可以在日誌中包含任何你想要的信息,包括:

  • err.status:HTTP 錯誤狀態碼。如果沒有提供,默認為 500
  • err.message:錯誤的詳細信息。
  • req.originalUrl:被請求的 URL。
  • req.path:請求 URL 的路徑部分。
  • req.method:請求的 HTTP 方法(GET、POST、PUT 等)。
  • req.ip:請求的遠程 IP 地址。

更新錯誤處理程序路由以包含 winston 日誌:

~/myApp/app.js
...
// 錯誤處理程序
app.use(function(err, req, res, next) {
  // 設置 locals,僅在開發環境提供錯誤信息
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // 包括 winston 日誌
  winston.error(
    `${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`
  );

  // 渲染錯誤頁面
  res.status(err.status || 500);
  res.render('error');
});
...

保存並關閉文件。

為了測試這個過程,嘗試訪問你項目中不存在的頁面。訪問不存在的頁面會引發 404 錯誤。在你的 Web 瀏覽器中,嘗試加載以下 URL:http://你的服務器 IP:3000/foo。由於 express-generator 創建的樣板,應用程序已經設置為對此類錯誤進行響應。

您的浏览器会显示以下错误消息:

当您查看 SSH 会话 A 中的控制台时,应该会有错误的日志条目。由于应用了 colorize 格式,很容易就能发现:

Output
[nodemon] starting `node bin/www` error: 404 - Not Found - /foo - GET - ::1 info: ::1 - - [25/Apr/2022:18:08:33 +0000] "GET /foo HTTP/1.1" 404 982 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36" info: ::1 - - [25/Apr/2022:18:08:33 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://localhost:3000/foo" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"

至于文件记录器,再次运行 tail 命令应该会显示新的日志记录:

  1. tail ~/myApp/logs/app.log

您会看到类似以下的消息:

{"level":"error","message":"404 - Not Found - /foo - GET - ::1","timestamp":"2022-04-25T18:08:33.508Z"}

错误消息包含了您特别指示 winston 记录的所有数据作为错误处理程序的一部分。此信息将包括错误状态 (404 – 未找到)、请求的 URL (localhost/foo)、请求方法 (GET)、发出请求的 IP 地址以及请求的时间戳。

结论

在本教程中,您构建了一个简单的 Node.js Web 应用,并集成了一个 Winston 日志记录解决方案,将作为提供应用程序性能洞察的有效工具。

您可以為應用程序建立更強大的日誌記錄解決方案,特別是當您的需求變得更加複雜時。要了解有關 Winston 傳輸的更多信息,請參見Winston 傳輸文檔。要創建自己的傳輸,請參見添加自定義傳輸。要創建供 HTTP 核心傳輸使用的 HTTP 端點,請參見winstond。要將 Winston 用作分析工具,請參見分析

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-winston-to-log-node-js-applications-on-ubuntu-20-04