使用 Pino 进行 ECS 日志记录
编辑使用 Pino 进行 ECS 日志记录编辑
此 Node.js 包为 pino 日志记录器提供了一种格式化程序,与 Elastic 通用模式 (ECS) 日志记录 兼容。结合 Filebeat 发货器,您可以在 Elastic Stack 中的同一个位置 监控所有日志。 支持 pino
6.x、7.x 和 8.x 版本。
设置编辑
步骤 1:安装编辑
$ npm install @elastic/ecs-pino-format
步骤 2:配置编辑
const { ecsFormat } = require('@elastic/ecs-pino-format'); const pino = require('pino'); const log = pino(ecsFormat(/* options */)); log.info('hi'); log.error({ err: new Error('boom') }, 'oops there is a problem'); // ...
这将 配置 Pino 的 |
请参见下面的用法讨论和示例。
步骤 3:配置 Filebeat编辑
将日志格式化为 ECS 后,使用 Filebeat 收集日志的最佳方法是
- 按照 Filebeat 快速入门
- 将以下配置添加到您的
filebeat.yaml
文件中。
对于 Filebeat 7.16+
filebeat.yaml。
filebeat.inputs: - type: filestream paths: /path/to/logs.json parsers: - ndjson: overwrite_keys: true add_error_key: true expand_keys: true processors: - add_host_metadata: ~ - add_cloud_metadata: ~ - add_docker_metadata: ~ - add_kubernetes_metadata: ~
使用 filestream 输入从活动日志文件中读取行。 |
|
如果发生冲突,来自解码 JSON 对象的值将覆盖 Filebeat 通常添加的字段(类型、源、偏移量等)。 |
|
Filebeat 在出现 JSON 反序列化错误的情况下会添加 "error.message" 和 "error.type: json" 密钥。 |
|
Filebeat 会递归地去除解码 JSON 中的点号密钥,并将它们扩展成一个分层对象结构。 |
|
处理器会增强您的数据。请参见 处理器 以了解更多信息。 |
对于 Filebeat < 7.16
filebeat.yaml。
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: ~
- 确保您的应用程序将日志记录到 stdout/stderr。
- 按照 在 Kubernetes 上运行 Filebeat 指南。
- 启用 基于提示的自动发现(取消
filebeat-kubernetes.yaml
中相应部分的注释)。 - 将这些注释添加到使用 ECS 日志记录器记录日志的 Pod 中。这将确保日志得到正确解析。
- 确保您的应用程序将日志记录到 stdout/stderr。
- 按照 在 Docker 上运行 Filebeat 指南。
- 启用 基于提示的自动发现。
- 将这些标签添加到使用 ECS 日志记录器记录日志的容器中。这将确保日志得到正确解析。
docker-compose.yml。
有关更多信息,请参见 Filebeat 参考。
用法编辑
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');
请参见可用的选项 以下。 |
运行此命令将生成类似于以下内容的日志输出
{"log.level":"info","@timestamp":"2023-10-14T02:07:47.901Z","process.pid":56645,"host.hostname":"pink.local","ecs.version":"8.10.0","message":"Hello world"} {"log.level":"warn","@timestamp":"2023-10-14T02:07:47.901Z","process.pid":56645,"host.hostname":"pink.local","ecs.version":"8.10.0","module":"foo","message":"From child"}
错误日志记录编辑
默认情况下,格式化程序会将一个 err
字段(该字段是一个 Error 实例)转换为 ECS 错误字段。 例如
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');
将产生(为了可读性而进行了美化)
% node examples/error.js | jq . { "log.level": "info", "@timestamp": "2021-01-26T17:02:23.697Z", ... "error": { "type": "Error", "message": "boom", "stack_trace": "Error: boom\n at Object.<anonymous> (..." }, "message": "oops" }
这类似于并覆盖了 Pino 的默认 err 序列化程序。可以通过 convertErr: false
选项禁用 err
字段的特殊处理
const log = pino(ecsFormat({ convertErr: false }));
HTTP 请求和响应日志记录编辑
使用 convertReqRes: true
选项,格式化程序会自动转换 Node.js 核心 请求 和 响应 对象(分别作为 req
和 res
字段传入)。(此选项替换了使用 req
和 res
Pino 序列化程序。)
const http = require('http'); const { ecsFormat } = require('@elastic/ecs-pino-format'); const pino = require('pino'); const log = pino(ecsFormat({ convertReqRes: true })); const server = http.createServer(function handler (req, res) { res.setHeader('Foo', 'Bar'); res.end('ok'); log.info({ req, res }, 'handled request'); }); server.listen(3000, () => { log.info('listening at https://127.0.0.1:3000'); }
这将使用 ECS HTTP 字段 生成包含请求和响应信息的日志。 例如
% node examples/http.js | jq . # using jq for pretty printing ... # run 'curl https://127.0.0.1:3000/' { "log.level": "info", "@timestamp": "2023-10-14T02:10:14.477Z", "process.pid": 56697, "host.hostname": "pink.local", "ecs.version": "8.10.0", "http": { "version": "1.1", "request": { "method": "GET", "headers": { "host": "localhost:3000", "user-agent": "curl/8.1.2", "accept": "*/*" } }, "response": { "status_code": 200, "headers": { "foo": "Bar" } } }, "url": { "full": "https://127.0.0.1:3000/", "path": "/" }, "client": { "address": "::ffff:127.0.0.1", "ip": "::ffff:127.0.0.1", "port": 49504 }, "user_agent": { "original": "curl/8.1.2" }, "message": "handled request" }
examples/ 目录 显示了使用请求和响应日志记录的示例程序:使用 Express、使用 pino-http 中间件包 等。
与 APM 进行日志关联编辑
此 ECS 日志格式化程序与 Elastic APM 集成。如果您的 Node 应用程序正在使用 Node.js Elastic APM 代理,那么会在日志记录中添加许多字段,以在 APM 服务或跟踪与日志记录数据之间进行关联
例如,运行 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 当前提供的挂钩不允许此包转换传递给 <logger>.child({ ... })
的字段。 这意味着,即使使用 convertReqRes
选项,对 <logger>.child({ req })
的调用也不会 将 该 req
转换为 ECS HTTP 字段。 对于使用 pino-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 日志记录格式输出的选项。