使用 Winston 进行 ECS 日志记录
编辑使用 Winston 进行 ECS 日志记录编辑
此 Node.js 包为 winston 日志记录器提供了一个格式化程序,与 Elastic 通用模式 (ECS) 日志记录 兼容。结合 Filebeat 发货程序,您可以在 Elastic Stack 中 集中监控所有日志。支持 winston
3.x 版本 >=3.3.3。
设置编辑
步骤 1:安装编辑
$ npm install @elastic/ecs-winston-format
步骤 2:配置编辑
const winston = require('winston'); const { ecsFormat } = require('@elastic/ecs-winston-format'); const logger = winston.createLogger({ format: ecsFormat(/* options */), transports: [ new winston.transports.Console() ] }); logger.info('hi'); logger.error('oops there is a problem', { err: new Error('boom') });
步骤 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 通常添加的字段(type、source、offset 等),以防发生冲突。 |
|
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 参考。
您可能想尝试使用 Node.js ECS 日志记录与 winston 的教程:使用 Filebeat 从 Node.js Web 应用程序中提取日志。
用法编辑
const winston = require('winston'); const { ecsFormat } = require('@elastic/ecs-winston-format'); const logger = winston.createLogger({ level: 'info', format: ecsFormat(/* options */), transports: [ new winston.transports.Console() ] }); logger.info('hi'); logger.error('oops there is a problem', { foo: 'bar' });
请参阅 下方 提供的可用选项。 |
运行此脚本(可在此处 获取)将产生与以下内容类似的日志输出
% node examples/basic.js {"@timestamp":"2023-10-14T02:14:17.302Z","log.level":"info","message":"hi","ecs.version":"8.10.0"} {"@timestamp":"2023-10-14T02:14:17.304Z","log.level":"error","message":"oops there is a problem","ecs.version":"8.10.0","foo":"bar"}
格式化程序处理对 JSON 的序列化,因此您无需添加 json 格式化程序。此外,格式化程序会自动生成时间戳,因此您无需添加 timestamp 格式化程序。
错误日志记录编辑
默认情况下,格式化程序将 err
元字段(它是 Error 实例)转换为 ECS 错误字段。例如
const winston = require('winston'); const { ecsFormat } = require('@elastic/ecs-winston-format'); const logger = winston.createLogger({ format: ecsFormat(), transports: [ new winston.transports.Console() ] }); const myErr = new Error('boom'); logger.info('oops', { err: myErr });
将产生(为了可读性而进行了美化)
% node examples/error.js | jq . { "@timestamp": "2021-01-26T17:25:07.983Z", "log.level": "info", "message": "oops", "error": { "type": "Error", "message": "boom", "stack_trace": "Error: boom\n at Object.<anonymous> (..." }, "ecs.version": "8.10.0" }
可以通过 convertErr: false
选项禁用对 err
元字段的特殊处理
... const logger = winston.createLogger({ format: ecsFormat({ convertErr: false }), ...
HTTP 请求和响应日志记录编辑
使用 convertReqRes: true
选项,格式化程序会自动将 Node.js 核心 请求 和 响应 对象分别转换为 req
和 res
元字段。
const http = require('http'); const winston = require('winston'); const { ecsFormat } = require('@elastic/ecs-winston-format'); const logger = winston.createLogger({ level: 'info', format: ecsFormat({ convertReqRes: true }), transports: [ new winston.transports.Console() ] }); const server = http.createServer(handler); server.listen(3000, () => { logger.info('listening at https://127.0.0.1:3000') }); function handler (req, res) { res.setHeader('Foo', 'Bar'); res.end('ok'); logger.info('handled request', { req, res }); }
这将生成使用 ECS HTTP 字段 的请求和响应信息的日志。例如
% node examples/http.js | jq . # using jq for pretty printing ... # run 'curl https://127.0.0.1:3000/' { "@timestamp": "2023-10-14T02:15:54.768Z", "log.level": "info", "message": "handled request", "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": { "path": "/", "full": "https://127.0.0.1:3000/" }, "client": { "address": "::ffff:127.0.0.1", "ip": "::ffff:127.0.0.1", "port": 49538 }, "user_agent": { "original": "curl/8.1.2" }, "ecs.version": "8.10.0" }
与 APM 的日志关联编辑
此 ECS 日志格式化程序与 Elastic APM 集成。如果您的 Node 应用程序正在使用 Node.js Elastic APM Agent,则会将一些字段添加到日志记录中,以关联 APM 服务或跟踪与日志记录数据。
- 在当前跟踪跨度存在时调用的日志语句(例如
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": "7fd75f0f33ff49aba85d060b46dcad7e", "transaction.id": "6c97c7c1b468fa05" }
这些 ID 与 APM 代理报告的跟踪数据相匹配。
可以通过 apmIntegration: false
选项显式禁用与 Elastic APM 的集成,例如
const logger = winston.createLogger({ format: ecsFormat({ apmIntegration: false }), // ... })
局限性和注意事项编辑
ecs-logging 规范 建议日志记录中的前三个字段应该是 @timestamp
、log.level
和 message
。从 1.5.0 版本开始,此格式化程序 不 遵循此建议。这是可能的,但需要为每个日志记录创建新的 ecsFields
对象。鉴于 ecs-logging 字段的排序是为了 人类可读性 并且不会影响互操作性,因此决定优先考虑性能。
参考编辑
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}
的默认值。
-
为 winston 创建一个格式化程序,以 ECS 日志记录格式输出。这是一种单一格式,它同时处理 ecsFields([options])
和 ecsStringify([options])
。以下两个等效
const { ecsFormat, ecsFields, ecsStringify } = require('@elastic/ecs-winston-format'); const winston = require('winston'); const logger = winston.createLogger({ format: ecsFormat(/* options */), // ... }); const logger = winston.createLogger({ format: winston.format.combine( ecsFields(/* options */), ecsStringify() ), // ... });
ecsFields([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}
的默认值。
-
为 winston 创建一个格式化程序,将日志记录信息对象上的字段转换为 ECS 日志记录格式。
ecsStringify([options])
编辑
为 winston 创建一个格式化程序,将日志记录序列化为 JSON。
这类似于 logform.json()
。它们都使用 safe-stable-stringify
包来生成 JSON。一些区别
- 此字符串化器会跳过序列化
level
字段,因为它不是 ECS 字段。 - Winston 提供了一个
replacer
,它将大整数转换为字符串。这样做的理由是 JavaScript JSON 解析器在解析大整数时会丢失精度。反对的理由是,大整数会从数字类型变为字符串类型,而不是数字类型。目前,此字符串化器不会将 BitInt 转换为字符串。