开发人员开始使用JavaScript编程时最常面临的首要任务是如何使用
console.log方法在控制台日志中记录事件。 在搜索有关调试JavaScript代码的信息时,您会找到数百篇博客文章以及StackOverflow上的说明,建议您通过
console.log方法“简单”将数据输出到控制台。 这是一种常见的做法,我不得不引入代码质量控制规则,例如
no-console ,以免在生产代码中留下随机的日志条目。 但是,如果您需要专门注册一个事件以提供其他信息怎么办?
本文讨论了需要维护日志的各种情况。 它显示了Node.js中
console.log和
console.error方法之间的区别,并演示了如何在不使用户控制台过载的情况下将日志记录功能传递给库。

使用Node.js的理论基础
console.log和
console.error方法可以在浏览器和Node.js中使用。 但是,使用Node.js时,要记住一件事。 如果您使用名为
index.js的文件在Node.js中创建以下代码,

然后在终端中使用
node index.js执行它,那么命令执行的结果将位于另一个之上:

尽管它们看起来很相似,但是系统对它们的处理方式有所不同。 如果您查看
Node.js文档中有关
console操作的部分,则会发现
console.log通过
stdout打印结果,而
console.error通过
stderr打印结果。
每个进程默认都可以使用三个流(
stream ):
stdin ,
stdout和
stderr 。
stdin流处理某个过程的输入,例如,单击按钮或重定向输出(有关更多信息,请参见下文)。 标准
stdout输出流用于输出应用程序数据。 最后,标准的
stderr错误流旨在显示错误消息。 如果您需要弄清楚
stderr以及何时使用,请阅读
本文 。
简而言之,它可以用于使用重定向(
> )和管道(
| )运算符来处理错误和诊断信息,而这些错误和诊断信息与应用程序的实际结果分开。 如果
>运算符允许您将命令结果的输出重定向到文件,则可以使用
2>运算符将
stderr错误流的输出重定向到文件。 例如,此命令将
Hello发送到
hello.log文件,
再见再发送到
error.log文件。


什么时候需要将事件写入日志?
现在,我们已经回顾了记录基础的技术方面,让我们继续进行各种需要注册事件的场景。 通常,这些方案属于以下几类之一:
本文仅讨论基于Node.js的最后三种方案。
记录服务器应用程序
记录服务器上发生的事件有多种原因。 例如,记录传入的请求可以使您获得有关用户遇到404错误的频率,其原因可能是什么或正在使用哪个
User-Agent客户端应用程序的统计信息。 您还可以找出错误发生的时间及其原因。
为了试验本文这一部分中给出的材料,您需要为该项目创建一个新目录。 在项目目录中,为要使用的代码创建
index.js ,并执行以下命令以启动项目并安装
express :

我们设置了一个带有中间件的服务器,该服务器将使用
console.log方法在控制台中注册每个请求。 我们将以
index.js放入
index.js文件中:

这使用
console.log('%O', req)将整个对象记录在日志中。 从内部结构的角度来看,
console.log方法使用
util.forma t,除了
%O util.forma支持其他占位符。 有关它们的信息可以在
Node.js文档中找到。
当执行
node index.js来启动服务器并切换到
localhost :3000时,控制台会显示很多不必要的信息:

如果改为使用
console.log('%s', req)以便不完全显示对象,则不会获得很多信息:

您可以编写自己的日志记录功能,该功能仅输出必要的数据,但是首先您需要确定需要哪些信息。 尽管事实上通常将重点放在消息的内容上,但实际上通常仍需要获取其他信息,其中包括:
- 时间戳-知道事件何时发生;
- 计算机/服务器名称-如果分布式系统正在运行;
- 进程标识符-如果正在使用
pm2运行多个Node进程; - 消息-带有某些内容的实际消息;
- 堆栈跟踪-如果记录了错误;
- 其他变量/信息。
另外,考虑到在任何情况下,所有内容都输出到
stdout和
stderr流,因此需要将日志保留在不同级别,并需要根据级别配置和过滤日志条目。
这可以通过访问
process不同部分并用JavaScript编写几行代码来实现。 但是,Node.js的卓越之处在于它已经具有一个
npm生态系统和几个可用于这些目的的库。 这些包括:
Pino通常是首选,因为它速度快且具有自己的生态系统。 让我们看看
pino如何帮助进行日志记录。 该库的另一个优点是
express-pino-logger程序包,它允许您注册请求。
安装
pino和
express-pino-logger :

之后,我们更新
index.js文件以使用事件记录器和中间件:

在此片段中,我们为
pino创建了事件
logger的实例,并将其传递给
express-pino-logger以创建新的跨平台事件记录软件,您可以使用它调用
app.use 。 另外,在
logger.info ,
console.log替换为
logger.info并且
logger.info logger.debug添加到路由中以显示不同级别的日志。
如果通过重复执行
node index.js重新启动服务器,则会在输出中得到不同的结果,其中每行/每行将以JSON格式输出。 同样,转到
localhost :3000以查看JSON格式的另一行。

在JSON格式的数据中,您可以找到前面提到的信息,例如时间戳。 另请注意,未显示
logger.debug消息。 为了使其可见,您需要更改默认日志级别。 创建记录器事件注册的实例之后,设置了值
process.env.LOG_LEVEL 。 这意味着您可以更改值或接受默认
info值。
LOG_LEVEL=debug node index.js运行
LOG_LEVEL=debug node index.js ,我们可以更改日志级别。
在此之前,有必要解决输出格式的问题,这对于当前的感知而言并不十分方便。 此步骤是有意的。 根据
pino哲学,出于性能原因,有必要将日记帐分录的处理转移到一个单独的进程中,并传递输出(使用
|运算符)。 该过程涉及将输出转换为更便于人类感知的格式,或者将其上传到云。 该任务由称为
transports的传输工具执行。 查阅
transports 包文档 ,看看为什么
stderr不会输出
pino错误。
要查看更具可读性的杂志,请使用
pino-pretty工具。 在终端中运行:

所有日志条目都使用
| 由
pino-pretty处理,因此“清除”了输出,该输出仅包含以不同颜色显示的重要信息。 如果再次查询
localhost :3000,则会出现一条
debug调试消息。

为了使日记帐分录更具可读性或将其转换,有许多传输工具。 它们甚至可以通过使用
pino-colada表情符号来显示。 这些工具将对本地开发有用。 当服务器投入生产时,可能有必要使用
其他工具传输日志数据,使用
>将其写入磁盘以进行进一步处理,或者使用特定命令(例如
tee同时执行两项操作。
该
文档还讨论了旋转日志文件,过滤日志数据并将其写入其他文件的问题。
图书馆日记
通过探索有效地组织服务器应用程序日志的方法,您可以对自己的库使用相同的技术。
问题在于,就库而言,您可能需要保留日志以进行调试,而无需加载客户端应用程序。 相反,如果需要调试,则客户端应该能够激活日志。 默认情况下,该库不应记录输出,从而赋予用户此权限。
express框架就是一个很好的例子。
express框架的内部结构中发生了许多过程,这可能引起在应用程序调试期间对它进行更深入研究的兴趣。
express 框架的
文档说,您可以将
DEBUG=express:*添加到命令的开头,如下所示:

如果将此命令应用于现有应用程序,则可以看到许多有助于调试的其他输出:

除非激活调试日志,否则看不到此信息。 为此有一个
debug包。 它可用于在“名称空间”中编写消息,并且如果库用户在其
DEBUG 环境变量中包含此名称空间或与之匹配的通配符,则将显示消息。 首先,您需要安装
debug库:

创建一个名为
random-id.j的新文件,该文件将模拟该库并将以下代码放入其中:

结果,将使用
mylib:randomid创建一个新的
debug事件记录器,然后在其中注册两个消息。 我们在上一节的
index.js使用它:

如果再次启动服务器,这次添加
DEBUG=mylib:randomid node index.js ,那么将显示“库”的调试日志条目:

如果库用户希望将调试信息放入
pino日志条目中,则可以使用
pino命令创建的名为
pino-debug的库来正确格式化这些条目。
安装库:

首次使用
debug之前,必须先初始化
pino-debug 。 最简单的方法是在运行脚本之前使用
-r 或 --require 标志来请求模块。 我们使用以下命令重新启动服务器(前提
pino-colada已安装
pino-colada ):

结果,库的调试日志条目将以与应用程序日志相同的方式显示:

命令行界面(CLI)输出
本文讨论的最后一种情况是登录命令行界面。 优选地,记录与程序逻辑有关的事件的日志与用于注册命令行界面数据的日志保持分开。 要记录与程序逻辑有关的任何事件,您需要使用特定的库,例如
debug 。 在这种情况下,您可以使用命令行界面重用程序逻辑,而不仅限于一种情况。
通过使用Node.js创建命令行界面 ,您可以添加各种颜色,可变值块或格式设置工具,以使该界面具有视觉吸引力。 但是,您需要牢记几种情况。
其中一位认为,该界面可以在连续集成系统(CI)的上下文中使用,在这种情况下,最好放弃颜色格式设置并在视觉上过度显示结果。 一些连续集成系统设置了
CI标志。 您可以使用
is-ci软件包来验证您是否处于连续集成系统中,该软件包支持多个此类系统。
一些库(例如
chalk )定义了连续的集成系统,并将彩色文本的输出覆盖到控制台。 让我们看看它是如何工作的。
使用npm
install chalk并创建一个名为
cli.js的文件。 将以下行放入文件中:

现在,如果您使用
node cli.js执行此脚本,则结果将以不同的颜色显示:

但是,如果您使用
CI=true node cli.js执行脚本,则文本的颜色格式将被取消:

在另一个值得记住的场景中,
stdout在终端模式下运行,即 数据输出到终端。 在这种情况下,可以使用
boxen很好地显示结果。 否则,输出很可能会重定向到文件或其他位置。
您可以通过查看相应流的
isTTY属性来检查终端模式下
stdin ,
stdout或
stderr流的操作。 例如,
process.stdout.isTTY 。
TTY意思是“打字机”,在这种情况下,它是专门为终端设计的。
三个线程中的每个线程的值可能会有所不同,具体取决于启动Node.js进程的方式。 有关详细信息,请参见
Node.js文档的“进程的输入/输出”部分 。
让我们看看
process.stdout.isTTY的值在不同情况下如何变化。
cli.js文件进行检查:

现在在终端中运行
node cli.js并看到单词
true ,然后消息以彩色字体显示:

之后,我们重新执行命令,但是将输出重定向到文件,然后查看内容:

这次,单词
undefined出现在终端中,随后以无色字体显示消息,因为
stdout流将其重定向到终端模式之外。 在此,
chalk使用了
supports-color工具,该工具从内部结构的角度检查
isTTY相应流的
isTTY 。

chalk工具可以自行完成这些操作。 但是,在开发命令行界面时,您应始终注意该界面在连续集成系统中工作或重定向输出的情况。 这些工具可帮助您在更高级别上使用命令行界面。 例如,可以以更结构化的方式来组织终端中的数据,如果
isTTY undefined ,则切换到更简单的分析方式。
结论
开始使用JavaScript并使用
console.log在控制台日志中输入第一行非常简单。 但是,在将代码部署到生产环境中之前,应该考虑使用日志的几个方面。 本文只是对组织事件日志中使用的各种方法和解决方案的介绍。 它不包含您需要了解的所有内容。 因此,建议关注成功的开源项目,并监视它们如何解决日志记录问题以及使用哪些工具。 现在尝试登录自己而不将数据输出到控制台。
如果您知道其他值得一提的工具,请在评论中写下它们。