这是继上一篇文章 “elasticsearch:node.js ecs 日志记录 - pino” 的续篇。我们继续上一篇文章来讲述使用 winston 包来针对 node.js 应用生成 ecs 向匹配的日子。此 node.js 软件包为 winston 记录器提供了格式化程序,与 elastic common schema (ecs) 日志记录兼容。结合 filebeat 发送器,你可以在 elastic stack 中的一处监控所有日志。支持 winston 3.x 版本 >=3.3.3。
设置
安装
npm install @elastic/ecs-winston-format
npm install winston
配置
winston-logging.js
const winston = require('winston');
const { ecsformat } = require('@elastic/ecs-winston-format');
const logger = winston.createlogger({
format: ecsformat(/* options */), // 1
transports: [
new winston.transports.console()
]
});
logger.info('hi');
logger.error('oops there is a problem', { err: new error('boom') });
- 将 ecs 格式化程序传递给 winston。
上面的代码的运行结果为:
配置 filebeat
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: // 5
- 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 以了解更多信息。
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 参考。
如何使用
winston-logging.js
const winston = require('winston');
const { ecsformat } = require('@elastic/ecs-winston-format');
const logger = winston.createlogger({
level: 'info',
format: ecsformat(/* options */), // 1
transports: [
new winston.transports.console()
]
});
logger.info('hi');
logger.error('oops there is a problem', { foo: 'bar' });
- 请参阅下面的可用选项。
node winston-logging.js | jq .
运行此脚本(可在此处获得)将产生类似上面内容的日志输出。
格式化程序负责将数据序列化为 json,因此你无需添加 json 格式化程序。此外,格式化程序会自动生成 timestamp,因此你无需添加 timestamp 格式化程序。
error logging
默认情况下,格式化程序会将 error 实例的 err 元字段转换为 ecs error 字段。例如例子:
winston-logging.js
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 });
可以通过 converterr: false 选项禁用对 err 元字段的特殊处理:
winston-logging.js
const winston = require('winston');
const { ecsformat } = require('@elastic/ecs-winston-format');
const logger = winston.createlogger({
format: ecsformat({converterr: false} ),
transports: [
new winston.transports.console()
]
});
const myerr = new error('boom');
logger.info('oops', { err: myerr });
http 请求和响应日志记录
使用 convertreqres: true 选项,格式化程序将在分别作为 req 和 res 元字段传递时自动转换 node.js 核 request 和 response 对象。
winston-logging.js
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 }), // 1
transports: [
new winston.transports.console()
]
});
const server = http.createserver(handler);
server.listen(3000, () => {
logger.info('listening at http://localhost:3000')
});
function handler (req, res) {
res.setheader('foo', 'bar');
res.end('ok');
logger.info('handled request', { req, res }); // 2
}
- 使用 convertreqres 选项
- 记录 req 和/或 res 元字段
这将使用 ecs http 字段生成包含请求和响应信息的日志。例如例子:
在上面,我们需要访问 http://localhost:3000 才能看到如上所示的日子信息。
使用 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": "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 提供了一个将 bigint 转换为字符串的 replacer。这样做的理由是 javascript json 解析器在解析 bigint 时会丢失精度。反对的理由是 bigint 将类型更改为字符串而不是数字。目前,此字符串化器不会将 bitint 转换为字符串。
发表评论