使用 Winston 进行 ECS 日志记录
编辑使用 Winston 进行 ECS 日志记录
编辑这个 Node.js 包为 winston 日志记录器提供了一个格式化器,兼容 Elastic Common Schema (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 通常添加的字段(类型、源、偏移量等)。 |
|
如果发生 JSON 解组错误,Filebeat 将添加 "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 参考。
您可能想尝试使用 Winston 进行 Node.js ECS 日志记录的教程:使用 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 格式化器。
错误日志记录
编辑默认情况下,格式化器会将作为 Error 实例的 err
元字段转换为 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
选项,当分别作为 req
和 res
元字段传递时,格式化器会自动转换 Node.js 核心 request 和 response 对象。
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 代理,则会将许多字段添加到日志记录中,以关联 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": "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
中创建一个新的 Object。鉴于 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 创建一个格式化器,该格式化器将日志记录 info 对象上的字段转换为 ECS 日志格式。
ecsStringify([options])
编辑为 winston 创建一个格式化器,该格式化器将日志记录字符串化/序列化为 JSON。
这与 logform.json()
类似。它们都使用 safe-stable-stringify
包来生成 JSON。一些差异
- 此字符串转换器会跳过序列化
level
字段,因为它不是 ECS 字段。 - Winston 提供一个
replacer
,将 bigint 转换为字符串。这样做的 原因是 JavaScript JSON 解析器在解析 bigint 时会丢失精度。反对的原因是 BigInt 会将其类型更改为字符串而不是数字。目前,此字符串转换器不会将 BitInt 转换为字符串。