摘要:將如下代碼寫入到文件中,并在環境里執行如圖雖然這兩個輸出看起來可能一樣,但系統實際上對它的處理方式有不同。如圖如果你沒有啟動調試日志,則不會看到任何這樣的日志輸出。
原文地址: https://www.twilio.com/blog/g...原文作者: DOMINIK KUNDEL
翻譯作者: icepy
翻譯出處: https://github.com/lightningm...
當你開始使用 JavaScript 做開發時,你可能學習到的第一件事情就是如何使用 console.log 將內容打印到控制臺。如果你搜索如何調試 JavaScript,你會發現數百個博客文章和 StackOverflow 的文章都指向簡單的 console.log 。因為這是一種常見的做法,我們甚至可以使用 no-console 這樣的規則來確保生產環境不會留下日志。但是,如果我們真的想要記錄這些信息呢?
在這篇博文中,我們將介紹你想要記錄信息的各種情況,Node.js 中的 console.log 和 console.error 之間的區別是什么,以及如何在不使用戶控制臺混亂的情況下在庫中發送日志記錄。
console.log(`Let"s go!`);Theory First: Important Details for Node.js
雖然你可以在瀏覽器和 Node.js 環境中使用 console.log 和 console.error,但在 Node.js 中使用時一定要記住一件重要的事情。
將如下代碼寫入到 index.js 文件中,并在 Node.js 環境里執行:
console.log("Hello there"); console.error("Bye bye");
如圖:
雖然這兩個輸出看起來可能一樣,但系統實際上對它的處理方式有不同。如果你檢查一下 console section of the Node.js documentation 你會發現 console.log 使用 stdout 打印而 console.error 則使用 stderr。
每一個進程都有三個可以使用的默認 streams,它們是 stdin ,stdout 和 stderr。 stdin 可以處理進程的輸入,例如按下按鈕或重定向輸出。stdout 可以用于處理進程的輸出。最后 stderr 則用于錯誤消息。如果你想了解 stderr 為什么存在以及何時使用它,可以訪問:When to use STDERR instead of STDOUT。
簡而言之,這允許我們使用重定向 > 和管道 | 運算符來處理與應用程序的實際結果分開的錯誤和診斷信息。而 > 允許我們將命令的輸出重定向到文件,2> 允許我們將 stderr 的輸出重定向到文件。我們來看一個例子,它會將 Hello there 重定向輸出到 hello.log ,Bye bye 重定向輸出到 error.log。:
$ node index.js > hello.log 2> error.log
如圖:
When Do You Want to Log?現在我們已經了解了日志記錄的基礎技術,那么讓我們來談談你可能想要記錄某些內容的不同例子,通常這些例子都屬于以下類別之一:
快速調試開發階段的意外行為
基于瀏覽器的分析和診斷日志記錄
記錄服務器應用程序傳入的請求以及可能發生的任何故障
某些庫的可選調試日志
CLI的進度輸出
我們將跳過本博文中的前兩篇文章,并將重點介紹基于Node.js的三篇文章。
Your Server Application Logs你希望在服務器上記錄內容的原因可能有多種,例如:記錄傳入的請求,統計信息,有多少404用戶正在訪問,另外你也想知道什么時候出錯以及為什么。
初始化項目:
$ npm init -y $ npm install express
讓我們設置一個帶有中間件的服務器,只需要 console.log 為你的請求提供打印:
const express = require("express"); const PORT = process.env.PORT || 3000; const app = express(); app.use((req,res,next) => { console.log("%o", req); next(); }); app.get("/", (req,res) => { res.send("hello world"); }); app.listen(PORT, () => { console.log("Server running on port %d", PORT); });
這里我們使用 console.log("%o", req); 來記錄整個對象。
當你運行 node index.js 并訪問 http://localhost:3000 你會注意到打印的很多信息并不是我們需要的。
如果將起更改為 console.log("%s",req) 我們也不會獲取太多的信息。
我們可以編寫自己的日志功能,只打印我們關心的信息。但讓我們先退一步,談談我們通常關心的事情。雖然這些信息經常成為我們關注的焦點,但實際上我們可能需要其他信息,例如:
時間戳-知道事情何時發生
計算機/服務器名稱-如果你運行的是分布式系統的話
進程ID-如果你使用 pm2 運行著多個 Node.js 進程
消息-包含某些內容的實際消息
堆棧追蹤
也許是一些額外的變量或信息
另外,既然我們知道打印最后都會落到 stdout 和 stderr 上,那么我們可能想要不同日志級別的記錄以及過濾它的能力。
我們可以通過訪問流程的各個部分并編寫一堆 JavaScript 代碼來獲取上述的信息,但 npm 生態已經給我們提供了各種各樣的庫來使用,例如:
pino
winston
roarr
bunyan
我個人喜歡 pino,因為它速度快,生態全。那么,讓我們來看一看 pino 是如何幫助我們記錄日志的。
$ npm install pino express-pino-logger
const express = require("express"); const pino = require("pino"); const expressPino = require("express-pino-logger"); const logger = pino({ level: process.env.LOG_LEVEL || "info"}); const expressLogger = expressPino({ logger }); const PORT = process.env.PORT || 3000; const app = express(); app.use(expressLogger); app.get("/", (req,res) => { logger.debug("Calling res.send") res.send("hello world"); }); app.listen(PORT, () => { logger.info("Server running on port %d", PORT); });
運行 node index.js 并訪問 http://localhost:3000 你可以看到一行一行的 JSON 輸出:
如果你檢查此 JSON ,你會看到前面提到的時間戳。你可能還注意到了我們 logger.debug 語句并未打印,那是因為我們必須更改默認日志級別才能使其可見,試試 LOG_LEVEL=debug node index.js 來調整日志級別。
在此之前我們還需要解決一下日志信息的可讀性,pino 遵循了一個理念,就是為了性能,你應該通過管道將輸出的處理移動到多帶帶的進程中,你可以去查看一下文檔,了解其中 pino 的錯誤為什么不會寫入 stderr。
讓我們使用 pino-pretty 工具來查看更易讀的日志:
$ npm install --save-dev pino-pretty $ LOG_LEVEL=debug node index.js | ./node_modules/.bin/pino-pretty
運行 LOG_LEVEL=debug node index.js | ./node_modules/.bin/pino-pretty 并訪問 http://localhost:3000。
如圖:
另外還有各種各樣的庫可以來美化你的日志,甚至你可以使用 pino-colada 用 emojis 來顯示它們。這些對于你的本地開發非常有用,在運行到生產服務器之后,你可能希望將日志的管道轉移到另外一個管道,使用 > 將它們寫入硬盤以便稍后處理它們。
比如:
$ LOG_LEVEL=debug node index.js | ./node_modules/.bin/pino-pretty | > success.log 2> s_error.logYour Library Logs
既然我們研究了如何有效的為服務器應用程序編寫日志,那么為什么不能將它用在我們的某些庫中呢?問題是,你的庫可能希望記錄用于調試的內容,但實際上不應該讓使用者的應用程序變得混亂。相反,如果需要調試某些東西,使用者應該能夠啟動日志。你的庫默認情況下不會處理這些,并將輸入輸出的操作留給使用者。
express 就是一個很好的例子。
在 express 框架下有很多事情要做,在調試應用程序時,你可能希望了解一下框架的內容。如果我們查詢文檔,你會注意到你可以在命令行的前面加上 DEBUG=express:* 來啟動。
$ DEBUG=express:* node index.js
如圖:
如果你沒有啟動調試日志,則不會看到任何這樣的日志輸出。這是通過一個叫 debug 的包來完成的。
$ npm install debug
讓我們創建一個新的文件 random-id.js 來使用它:
const debug = require("debug"); const log = debug("mylib:randomid"); log("Library loaded"); function getRandomId() { log("Computing random ID"); const outcome = Math.random() .toString(36) .substr(2); log("Random ID is "%s"", outcome); return outcome; } module.exports = { getRandomId };
這里會創建一個帶有命名空間為 mylib:randomid 的 debug 記錄器,然后會將這兩種消息記錄上去。
我們可以在 index.js 文件中引用它:
const express = require("express"); const pino = require("pino"); const expressPino = require("express-pino-logger"); const randomId = require("./random-id"); const logger = pino({ level: process.env.LOG_LEVEL || "info"}); const expressLogger = expressPino({ logger }); const PORT = process.env.PORT || 3000; const app = express(); app.use(expressLogger); app.get("/", (req,res) => { logger.debug("Calling res.send") const id = randomId.getRandomId(); res.send(`hello world [${id}]`); }); app.listen(PORT, () => { logger.info("Server running on port %d", PORT); });
然后使用 DEBUG=mylib:randomid node index.js 來重新運行你的 index.js 文件,如圖:
有意思的是,如果你的庫使用者想把這些調試信息集成到自己的 pino 日志中去,那么他們可以使用一個叫 pino-debug 的庫來正確的格式化這些日志。
$ npm install pino-debug
pino-debug 在我們第一次使用之前需要初始化一次 debug,最簡單的方法就是在啟動之前使用 Node.js 的 -r 或 --require 命令來初始化。
$ DEBUG=mylib:randomid node -r pino-debug index.js | ./node_modules/.bin/pino-colada
如圖:
Your CLI Output我將在這篇博文中介紹最后一個案例,針對 CLI 的日志記錄。我的理念是將邏輯日志和你的 CLI 輸出分開。對于任何邏輯日志來說,你應該使用類似 debug 這樣的包。這樣你或其他人就可以重寫邏輯,而不受 CLI 的約束。
一種情況是你的 CLI 在持續集成的系統中被使用,因此你可能希望刪除各種花里胡哨的輸出。有些 CI 系統設置了一個被稱為 CI 的環境標志。如果你想更安全的檢查自己是否在 CI 系統中,你可以使用 is-ci 這個庫。
有些庫例如 chalk 已經為你檢查了 CI 并幫你刪除了顏色。
$ npm install chalk
const chalk = require("chalk"); console.log("%s Hi there", chalk.cyan("INFO"));
運行 node cli.js,如圖:
當你運行 CI=true node cli.js,如圖:
你要記住的是另外一個場景 stdout 能否在終端模式中運行。如果是這種情況,我們可以使用類似 boxen 的東西來顯示所有漂流的輸出。但如果不是,則可能會將輸出重定向到文件或輸出到其他地方。
你可以使用 isTTY 來檢查 stdout,stdin,stderr 是否在終端模式。
如:
process.stdout.isTTY
根據 Node.js 的啟動方式,這個三個的值可能不同。你可以在文檔中找到更多關于它的信息。
讓我們看看 process.stdout.isTTY 在不同情況下的變化:
const chalk = require("chalk"); console.log(process.stdout.isTTY); console.log("%s Hi there", chalk.cyan("INFO"));
然后運行 node index.js ,如圖:
之后運行相同的內容,但將其輸出重定向到一個文件中,這次你會看見它會打印一個 undefined 后面跟著一個簡單的無色消息。
這是因為 stdout 關閉了終端模式下 stdout 的重定向。
chalk 使用了 supports-color ,它會在引擎里檢查各個流的 isTTY。
像 chalk 這樣的庫已經幫你處理了這些行為,但在開發 CLI 的過程中還是要注意,在 CI 模式下運行或輸出被重定向的問題。
例如,你可以在終端以一種漂亮的方式來排列數據,如果 isTTY 為 undefined 時,則切換到更容易解析的方式上。
In Summary在 JavaScript 中使用 console.log 是非常快的,但當你將代碼部署到生產環境時,你應該要考慮更多關于記錄的內容。
本文僅僅是介紹了各種方法和可用的日志記錄解決方案,它不包含你需要知道的一切。
因此我建議你多看一看你喜歡的開源項目,看看它們是如何解決日志記錄問題以及它們所使用的工具。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104630.html
摘要:化應用程序此示例的目的是向你展示如何將應用程序放入容器中,該指南旨在用于開發,而不用于生產部署,本指南還假設你有一個有效的安裝,并且基本了解應用程序的結構。 Docker化Node.js Web應用程序 此示例的目的是向你展示如何將Node.js應用程序放入Docker容器中,該指南旨在用于開發,而不用于生產部署,本指南還假設你有一個有效的Docker安裝,并且基本了解Node.js應...
摘要:為指定事件注冊一個監聽器,接受一個字符串和一個回調函數。發射事件,傳遞若干可選參數到事件監聽器的參數表。為指定事件注冊一個單次監聽器,即監聽器最多只會觸發一次,觸發后立刻解除該監聽器。 1.Node.js 簡介 Node.js 其實就是借助谷歌的 V8 引擎,將桌面端的 js 帶到了服務器端,它的出現我將其歸結為兩點: V8 引擎的出色; js 異步 io 與事件驅動給服務器帶來極高...
摘要:通過跟蹤請求的處理過程,來對應用系統在前后端處理服務端調用的性能消耗進行跟蹤,關于的介紹可以看這個鏈接,大規模分布式系統的跟蹤系統作者刀把五鏈接來源知乎著作權歸作者所有。 手把手教你搭APM之Skywalking 前言 什么是APM?全稱:Application Performance Management 可以參考這里: 現代APM體系,基本都是參考Google的Dapper(大規模...
摘要:最近公司入職了個實習同學,本期收錄了關于實習同學如何快速掌握熟練工程師的工作流程開發套路來自我提升的多條精彩內容。想知道我接下來會寫些什么歡迎訂閱我的掘金專欄或知乎專欄前端周刊讓你在前端領域跟上時代的腳步。 showImg(https://segmentfault.com/img/bVQ9QE?w=1920&h=1080); 共 1818 字,讀完需 3 分鐘。本周 JSConf Ch...
摘要:但是究竟什么是區塊鏈呢如何基于區塊鏈構建去中心化的應用這個系列文章很有料,有圖文解釋有代碼實例,非常適合入門。想知道我接下來會寫些什么歡迎訂閱我的掘金專欄或知乎專欄前端周刊讓你在前端領域跟上時代的腳步。 showImg(https://segmentfault.com/img/remote/1460000010417511); 共 2044 字,讀完需 4 分鐘。本期以《提問的智慧》作...
閱讀 3757·2021-11-24 09:39
閱讀 2953·2021-11-16 11:49
閱讀 2076·2019-08-30 13:54
閱讀 1105·2019-08-30 13:03
閱讀 1091·2019-08-30 11:10
閱讀 718·2019-08-29 17:10
閱讀 1248·2019-08-29 15:04
閱讀 1212·2019-08-29 13:02