node.js json
by Gajus Kuizinas
由Gajus Kuizinas
The past 8 months I have been creating GO2CINEMA. This web application allows users to discover showtimes. They can also book cinema tickets across the world.
在过去的8个月中,我一直在创建GO2CINEMA 。 该Web应用程序允许用户发现放映时间。 他们还可以预订世界各地的电影票。
The platform grew to over +50 distinct services. The services support data aggregation, normalization, validation, analysis, distribution, invalidation, and more. Some of these services run in high replication environments, into the hundreds.
该平台增长到超过50种独特的服务。 这些服务支持数据聚合,规范化,验证,分析,分发,失效等。 其中一些服务可在高度复制的环境中运行,成百上千种。
I needed to know when things break. And I needed to be able to correlate logs across all these services to identify the issue.
我需要知道什么时候坏了。 而且我需要能够将所有这些服务之间的日志关联起来以识别问题。
I needed a logger that did not exist.
我需要一个不存在的记录器。
For a long time I have been a big fan of using debug. Debug is simple to use, works in Node.js and browsers, does not require configuration and it is fast. However, problems arise when you need to parse logs. Anything but one-line text messages cannot be parsed in a safe way.
长期以来,我一直非常喜欢使用debug 。 调试简单易用,可在Node.js和浏览器中运行,不需要配置,而且速度很快。 但是,当您需要解析日志时会出现问题。 除了单行文本消息外,其他任何内容都无法以安全的方式进行解析。
To log structured data, I have been using Winston and Bunyan. These packages are great for application-level logging. I have preferred Bunyan because of the Bunyan CLI program used to pretty-print logs.
为了记录结构化数据,我一直在使用Winston和Bunyan 。 这些软件包非常适合应用程序级日志记录。 我更喜欢Bunyan,因为Bunyan CLI程序用于漂亮地打印日志。
However, these packages require program-level configuration. When constructing an instance of a logger, you need to define the transport and the log-level. This makes them unsuitable for use in code designed for use by other applications.
但是,这些程序包需要程序级别的配置。 构造记录器实例时,需要定义传输方式和日志级别。 这使得它们不适合在设计用于其他应用程序的代码中使用。
Then there is pino. Pino is a fast JSON logger. It’s CLI program is equivalent to Bunyan. It decouples transports, and it has a sane default configuration. Yet you still need to instantiate logger instance at the application-level. This makes it more suitable for application-level logging like Winston and Bunyan.
再就是皮诺 。 Pino是一个快速的JSON记录器。 它的CLI程序等效于Bunyan。 它使传输解耦,并且具有合理的默认配置。 但是,您仍然需要在应用程序级别实例化记录器实例。 这使其更适合Winston和Bunyan等应用程序级别的日志记录。
I needed a logger that:
我需要一个记录器:
Separates code from configuration
All configuration is stored in the environment variables
所有配置都存储在环境变量中
Has a CLI program
有一个CLI程序
In other words a logger that:
换句话说,记录器会:
… and it needs to be loud when things really break.
……当事情真的破裂时,它必须很大声 。
Roarr is this logger.
咆哮是这个记录器。
A logger must have an API that is simple to remember and produces predictable results. Roarr achieves this by restricting the surface of the API.
记录器必须具有易于记忆并产生可预测结果的API。 Roarr通过限制API的表面来实现这一目标。
Roarr logging is disabled by default. To enable logging, you must start the program with an environment variable ROARR_LOG
set to true
:
默认情况下,Roarr日志记录处于禁用状态。 要启用日志记录,必须在环境变量ROARR_LOG
设置为true
启动程序:
ROARR_LOG=true node ./index.js
All Roarr configuration is done using environment variables. The developer cannot disable logging or set logging level at the application level. This is a good thing – filtering, formatting and augmenting logs belongs to the out of process transports. This ensures that you never need to touch the code to change the logging behavior.
所有Roarr配置均使用环境变量完成。 开发人员无法禁用日志记录或在应用程序级别设置日志记录级别。 这是一件好事–过滤,格式化和扩充日志属于进程外传输 。 这样可以确保您无需触摸代码即可更改日志记录行为。
Roarr API is restricted to two parameters (plus printf formatting arguments).
Roarr API被限制为两个参数(加上printf格式化参数)。
Arguments after the message parameter are used to enable printf message formatting. Printf arguments must be of a primitive type (string | number | boolean | null
). There can be up to 9 printf arguments (or 8 if the first parameter is the context object).
message参数后面的参数用于启用printf消息格式。 Printf参数必须是原始类型( string | number | boolean | null
)。 最多可以有9个printf参数(如果第一个参数是上下文对象,则为8)。
In practice, this translates to the following usage:
实际上,这转换为以下用法:
import log from 'roarr';log('foo');log('bar %s', 'baz');const debug = log.child({ level: 'debug'});debug('qux');debug({ quuz: 'corge'}, 'quux');
The context parameter contains arbitrary user-defined data used to identify the context in which a log message was produced. It can contain application name, package name, task ID, host name, program instance name and other data.
context参数包含用于标识在其中生成日志消息的上下文的任意用户定义的数据。 它可以包含应用程序名称,程序包名称,任务ID,主机名,程序实例名称和其他数据。
This produces an output:
产生输出:
{"context":{},"message":"foo","sequence":0,"time":1506776210000,"version":"1.0.0"}{"context":{},"message":"bar baz","sequence":1,"time":1506776210000,"version":"1.0.0"}{"context":{"level":"debug"},"message":"quux","sequence":2,"time":1506776210000,"version":"1.0.0"}{"context":{"level":"debug","quuz":"corge"},"sequence":3,"message":"quux","time":1506776210000,"version":"1.0.0"}
This output is designed for consumption of the log transports.
此输出是为消耗日志传输而设计的。
To inspect the logs at the development time, use roarr pretty-print
program.
要在开发时检查日志,请使用roarr pretty-print
程序。
The output that the pretty-print CLI program produces looks like this:
漂亮打印的CLI程序产生的输出如下所示:
The CLI program relies on a set of conventions to structure the data.
CLI程序依赖于一组约定来构造数据。
Roarr CLI program has a couple of other commands and options. See roarr --help
for more information.
Roarr CLI程序还有一些其他命令和选项。 有关更多信息,请参见roarr --help
。
To avoid code duplication, you can use a singleton pattern to export a logger instance with predefined context properties. For instance, describing the application.
为避免代码重复,可以使用单例模式导出具有预定义上下文属性的记录器实例。 例如,描述应用程序。
I recommend to create a file Logger.js
in the base project directory. You can use this file to create a child instance of Roarr with context parameters describing the project and the initialization.
我建议在基础项目目录中创建一个文件Logger.js
。 您可以使用此文件创建Roarr的子实例,该实例具有描述项目和初始化的上下文参数。
/** * @file Example contents of a Logger.js file. */
import Roarr from 'roarr';import ulid from 'ulid';
// Instance ID is useful for correlating logs in high concurrency environment. See `roarr augment --append-instance-id` option as an alternative way to// append instance ID to all logs.const instanceId = ulid();
// The reason we are using `global.ROARR.prepend` as opposed to `roarr#child`// is because we want this information to be prepended to all logs, including// those of the "my-application" dependencies.global.ROARR.prepend = { ...global.ROARR.prepend, application: 'my-application', instanceId};
export default Roarr.child({ // .foo property is going to appear only in the logs that are created using // the current instance of a Roarr logger. foo: 'bar'});
If you are developing a code designed to be consumed by other applications and modules, you should avoid using global.ROARR
. Although there are valid use cases.
如果要开发旨在供其他应用程序和模块使用的global.ROARR
,则应避免使用global.ROARR
。 尽管存在有效的用例 。
You should still start the project by defining a Logger.js
file and use log.child
instead.
您仍然应该通过定义Logger.js
文件并使用log.child
来启动项目。
/** * @file Example contents of a Logger.js file. */
import Roarr from 'roarr';
export default Roarr.child({ domain: 'database', package: 'my-package'});
Roarr does not have reserved context property names. However, I encourage use of the conventions.
Roarr没有保留的上下文属性名称。 但是,我鼓励使用约定 。
The roarr pretty-print
CLI program is using the context property names suggested in the conventions. This will pretty-print the logs for the developer inspection purposes.
roarr pretty-print
CLI程序正在使用约定中建议的上下文属性名称。 这将漂亮地打印日志,以供开发人员检查。
Roarr is designed to print all or no logs. Refer to the ROARR_LOG
environment variable documentation.
Roarr旨在打印全部或不打印日志。 请参阅ROARR_LOG
环境变量文档。
To filter logs you need to use a JSON processor, such as jq.
要过滤日志,您需要使用JSON处理器,例如jq 。
jq
allows you to filter JSON messages using select(boolean_expression)
:
jq
允许您使用select(boolean_expression)
过滤JSON消息:
ROARR_LOG=true node ./index.js | jq 'select(.context.logLevel == "warning" or .context.logLevel == "error")'
The result is the only log message that has either a “warning” or “error” log level. You can combine jq with the Roarr CLI program to focus on a specific error message:
结果是唯一具有“警告”或“错误”日志级别的日志消息。 您可以将jq与Roarr结合使用 CLI程序专注于特定的错误消息:
ROARR_LOG=true node ./index.js | jq 'select(.context.package == "usus")' | roarr pretty-print
By now you probably have a hang of what a log message “context” is. It is a key-value object defining environment variables at the time of logging the message.
到目前为止,您可能对日志消息“上下文”一无所知。 它是键值对象,在记录消息时定义了环境变量。
In some cases, it might be useful to prepend properties to the context object globally for all messages at runtime. For instance, you have a task running program and you want to associate all logs that have printed during the time of the task execution.
在某些情况下,在运行时为所有消息全局在上下文对象之前添加属性可能很有用。 例如,您有一个正在运行的任务程序,并且想要关联在任务执行期间已打印的所有日志。
You can do this using global.ROARR.prepend:
您可以使用global.ROARR.prepend:
做到这global.ROARR.prepend:
import log from 'roarr';import foo from 'foo';const taskIds = [ 1, 2, 3];for (const taskId of taskIds) { global.ROARR = global.ROARR || {}; global.ROARR.prepend = { taskId }; log('starting task ID %d', taskId); // In this example, `foo` is an arbitrary third-party dependency that is using // roarr logger. foo(taskId); log('successfully completed task ID %d', taskId); global.ROARR.prepend = {};}
Produces output:
产生输出:
{"context":{"taskId":1},"message":"starting task ID 1","sequence":0,"time":1506776210000,"version":"1.0.0"}{"context":{"taskId":1},"message":"foo","sequence":1,"time":1506776210000,"version":"1.0.0"}{"context":{"taskId":1},"message":"successfully completed task ID 1","sequence":2,"time":1506776210000,"version":"1.0.0"}[...]
If you are using a central log aggregator, you can easily find all logs associated with a particular task. This is useful if you are investigating an error that has terminated a task early.
如果使用中央日志聚合器 ,则可以轻松找到与特定任务关联的所有日志。 如果您正在调查已提早终止任务的错误,这将很有用。
In most logging libraries a transport runs in-process to perform an operation with the finalized log line. For example, a transport might send the log line to a standard syslog server after processing and reformatting it.
在大多数日志库中,传输是在进程内运行的,以对最终的日志行执行操作。 例如,传输可能在处理并重新格式化其格式后将日志行发送到标准syslog服务器。
Roarr does not support in-process transports because Node processes are single threaded processes. Roarr offloads the handling of logs to external processes so the threading capabilities of the OS or other CPUs can be used.
Roarr不支持进程内传输,因为Node进程是单线程进程。 Roarr将日志处理工作转移到外部进程,因此可以使用OS或其他CPU的线程功能。
Depending on your configuration, consider one of the following log transports:
根据您的配置,请考虑以下日志传输之一:
For aggregating at a process level
用于在流程级别进行汇总
Written in Go
用Go写
For aggregating at a process level
用于在流程级别进行汇总
Written in JavaScript
用JavaScript编写
For aggregating logs at a container orchestration level such as Kubernetes
用于在容器编排级别(例如Kubernetes)聚合日志
Written in Ruby
用Ruby写
In the case of Fluentd and Kubernetes, aggregating all logs to ElasticSearch is as simple as creating a single DaemonSet.
对于Fluentd和Kubernetes,将所有日志聚合到ElasticSearch就像创建单个DaemonSet一样简单。
Roarr codebase is not complicated. The success of the project such as Roarr depends a lot on the scale effect. The more dependencies use Roarr, the more value it provides.
咆哮的代码库并不复杂。 像Roarr这样的项目的成功很大程度上取决于规模效应。 使用Roarr的依赖项越多,它提供的价值就越大。
Enabling logging allows instant gathering of all the data about applications. Their components required to monitor application health and trace issues.
启用日志记录可以即时收集有关应用程序的所有数据。 它们的组件需要监视应用程序的运行状况并跟踪问题。
In a sense, I am lucky. Over many years, I have developed an abstraction for almost every component that composes my Node.js and browser applications.
从某种意义上说,我很幸运。 多年来,我已经为构成Node.js和浏览器应用程序的几乎每个组件开发了一种抽象。
These range from database client, DOM evaluator, HTTP server process manager, and so forth. This allows me to quickly reap the benefits of a package such as Roarr.
这些范围包括数据库客户端 , DOM评估程序 , HTTP服务器进程管理器等。 这使我可以快速获得Roarr这样的软件包的好处。
Time will show whether the rest of the community adopts Roarr with equal passion.
时间将显示社区其他成员是否同样热情地采用Roarr。
I got some initial critique about the use of a generic, “cutesy” name such as “roarr”. The primary reason for choosing the name is to identify the package as loud. Roarr logs cannot be suppressed, just like the roar of a tiger.
我对使用诸如“吼声”之类的通用“易学”名称有一些最初的批评。 选择名称的主要原因是要确定包装是否响亮 。 咆哮的原木不能像老虎的咆哮一样被抑制。
From the practical perspective, “ROARR” is a safe term to grep and reserve in the global environment.
从实用的角度来看,“ROARR”是一个安全术语用grep和储备在全球环境中。
I am standardizing Roarr across all Node.js applications and packages that I maintain.
我正在对我维护的所有Node.js应用程序和包进行标准化Roarr 。
I expect that this will improve my ability to quickly detect issues. It will help me to reconfigure logging settings of the existing applications — without modifying the source code to adjust the log volumes. And it will improve my day-to-day experience of tailing application logs.
我希望这将提高我快速发现问题的能力。 这将帮助我重新配置现有应用程序的日志记录设置,而无需修改源代码以调整日志量。 它将改善我日常跟踪应用程序日志的经验。
Logging is one of the scariest parts of the application development. Roarr makes it less scary.
日志记录是应用程序开发中最可怕的部分之一。 咆哮声使其不那么恐怖。
You can support my open-source work and me writing technical articles through Buy Me A Coffee and Patreon. You’ll have my eternal gratitude ?
您可以通过Buy Me A Coffee和Patreon支持我的开源工作以及撰写技术文章。 你会永远感激我吗?
翻译自: https://www.freecodecamp.org/news/roarr-the-perfect-json-logger-node-js-and-browser-935180bda529/
node.js json