在今天的文章中,我来详细描述如何使用 node.js 来生成 ecs 相兼容的日子。ecs 相兼容的日志符合易于 elasticsearch 对日志进行分析并进行可视化。
介绍
node.js ecs 记录器是你最喜欢的日志库的格式化插件。它们可轻松将你的日志格式化为与 ecs 兼容的 json。结合 filebeat,你可以将日志直接发送到 elasticsearch,并利用 kibana 的日志应用程序在一个地方检查所有日志。
node.js ecs 日志格式化程序记录结构化的 json,并支持对来自 node.js 核心和流行 web 框架的错误对象和 http 请求和响应对象进行序列化。最小日志记录包括以下字段:
{
"@timestamp": "2021-01-13t21:32:38.095z",
"log.level": "info",
"message": "hi",
"ecs.version": "8.10.0"
}
有关如何生成 ecs 相兼容的日子,我们有如下的几种方法。
在今天的文章里,我们先来介绍 pino。
使用 pino 进行 ecs 日志记录
此 node.js 包为 pino 记录器提供了格式化程序,与 elastic common schema (ecs) 日志记录兼容。结合 filebeat 发送器,你可以在 elastic stack 中的一处监控所有日志。支持 pino 6.x、7.x 和 8.x 版本。
设置
安装
npm install @elastic/ecs-pino-format
npm install pino
配置
const { ecsformat } = require('@elastic/ecs-pino-format');
const pino = require('pino');
const log = pino(ecsformat(/* options */)); // 1
log.info('hi');
log.error({ err: new error('boom') }, 'oops there is a problem');
// ...
- 这将配置 pino 的 formatters、messagekey 和 timestamp 选项。
配置 filebeat
1)filebeat 7.16+。
filebeat.yml
filebeat.inputs:
- type: filestream # 1
paths: /path/to/logs.json
parsers:
- ndjson:
overwrite_keys: true # 2
add_error_key: true # 3
expand_keys: true # 4
processors:
- add_host_metadata: ~
- add_cloud_metadata: ~
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
- 使用 filestream 输入从活动日志文件中读取行。
- 如果发生冲突,解码的 json 对象的值将覆盖 filebeat 通常添加的字段(type, source, offset 等)。
- 如果发生 json 解组错误,filebeat 将添加 “error.message” 和 “error.type: json” 键。
- filebeat 将递归地从解码的 json 中去掉点键,并将其扩展为分层对象结构。
- 处理器可增强你的数据。请参阅 processors 以了解更多信息。
2)filebeat < 7.16
filebeat.yml
filebeat.inputs:
- type: log
paths: /path/to/logs.json
json.keys_under_root: true
json.overwrite_keys: true
json.add_error_key: true
json.expand_keys: true
processors:
- add_host_metadata: ~
- add_cloud_metadata: ~
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
有关更多信息,请参阅 filebeat 参考。
更多关于如何配置 filebeat,请参考文章 “beats:使用 filebeat 从 python 应用程序中提取日志”。
如何使用
我们创建如下的一个简单的应用:
pino-logging.js
const { ecsformat } = require('@elastic/ecs-pino-format');
const pino = require('pino');
const log = pino(ecsformat(/* options */));
log.info('hello world');
const child = log.child({ module: 'foo' });
child.warn('from child');
运行上面的代码:
node pino-logging.js
$ node pino-logging.js
{"log.level":"info","@timestamp":"2024-07-08t01:32:42.947z","process.pid":43946,"host.hostname":"liuxgm.local","ecs.version":"8.10.0","message":"hello world"}
{"log.level":"warn","@timestamp":"2024-07-08t01:32:42.947z","process.pid":43946,"host.hostname":"liuxgm.local","ecs.version":"8.10.0","module":"foo","message":"from child"}
在配置 ecsformat 时,我们在下面将会看到更多的选项。
error logging
默认情况下,格式化程序会将 error 实例的 err 字段转换为 ecs error 字段。例如:
pino-logging.js
const { ecsformat } = require('@elastic/ecs-pino-format');
const pino = require('pino');
const log = pino(ecsformat());
const myerr = new error('boom');
log.info({ err: myerr }, 'oops');
将产生(为了便于阅读,打印得很漂亮):
这类似于并覆盖了 pino 的默认 err 序列化器。可以通过 converterr: false 选项禁用对 err 字段的特殊处理:
const log = pino(ecsformat({ converterr: false }));
http 请求和响应日志记录
使用 convertreqres: true 选项,格式化程序将在分别作为 req 和 res 字段传递时自动转换 node.js 核心 request 和 response 对象。(此选项取代了 req 和 res pino 序列化器的使用。)
pino-logging.js
const http = require('http');
const { ecsformat } = require('@elastic/ecs-pino-format');
const pino = require('pino');
const log = pino(ecsformat({ convertreqres: true })); // 1
const server = http.createserver(function handler (req, res) {
res.setheader('foo', 'bar');
res.end('ok');
log.info({ req, res }, 'handled request'); // 2
});
server.listen(3000, () => {
log.info('listening at http://localhost:3000');
})
- 使用 convertreqres 选项
- 使用 req 和/或 res 字段进行记录
这将使用 ecs http 字段生成包含请求和响应信息的日志。例如:
为了生成上面的日子,我们需要访问本地的服务器 http://localhost:3000/.
examples/ 目录展示了使用请求和响应日志的示例程序:使用 express、使用 pino-http 中间件包等。
使用 apm 进行日志关联
此 ecs 日志格式化程序与 elastic apm 集成。如果你的 node 应用正在使用 node.js elastic apm agent,则会将多个字段添加到日志记录中,以关联 apm 服务或跟踪和日志数据:
- 当前跟踪 span 时调用的日志语句(例如 logger.info(...))将包括跟踪字段 — trace.id、transaction.id、span.id。
- 由 apm 代理确定或在 apm 代理上配置的多个服务标识符字段允许在 kibana 中的服务和日志之间进行交叉链接 — service.name、service.version、service.environment、service.node.name。
- event.dataset 在 elastic observability 应用中启用日志率异常检测。
例如,运行 examples/http-with-elastic-apm.js 和 curl -i localhost:3000/ 会产生包含以下内容的日志记录:
% node examples/http-with-elastic-apm.js | jq .
...
"service.name": "http-with-elastic-apm",
"service.version": "1.4.0",
"service.environment": "development",
"event.dataset": "http-with-elastic-apm",
"trace.id": "9f338eae7211b7993b98929046aed21d",
"transaction.id": "2afbef5642cc7a3f",
...
这些 id 与 apm 代理报告的跟踪数据相匹配。
可以通过 apmintegration: false 选项明确禁用与 elastic apm 的集成,例如:
const log = pino(ecsformat({ apmintegration: false }));
限制和注意事项
ecs-logging 规范建议日志记录中的前三个字段必须是 @timestamp、log.level 和 message。pino 不提供将 message 字段放在前面的机制。鉴于 ecs-logging 字段的排序是为了便于阅读,并且不会影响互操作性,因此这并不是一个重大问题。
pino 当前提供的钩子(hook)不允许此包转换传递给 <logger>.child({ ... }) 的字段。这意味着,即使使用 convertreqres 选项,对 <logger>.child({ req }) 的调用也不会将该 req 转换为 ecs http 字段。对于执行此操作的 pino-http 用户来说,这是一个轻微的限制。
参考
ecsformat([options])
- options {type-object} 支持以下选项:
- converterr {type-boolean} 是否将记录的 err 字段转换为 ecs 错误字段。默认值:true。
- convertreqres {type-boolean} 是否将记录的 req 和 res http 请求和响应字段记录到 ecs http、用户代理和 url 字段。默认值:false。
- apmintegration {type-boolean} 是否启用 apm 代理集成。默认值:true。
- servicename {type-string} “service.name” 值。如果指定,则覆盖来自活动 apm 代理的任何值。
- serviceversion {type-string} “service.version” 值。如果指定,则覆盖来自活动 apm 代理的任何值。
- serviceenvironment {type-string} “service.environment” 值。如果指定,则覆盖来自活动 apm 代理的任何值。
- servicenodename {type-string} “service.node.name” 值。如果指定,则将覆盖来自活动 apm 代理的任何值。
- eventdataset {type-string} “event.dataset”值。如果指定,则将覆盖使用 ${serviceversion} 的默认设置。
为 pino(...) 创建选项,用于配置 ecs 日志记录格式输出。
发表评论