我试图更好地理解如何在不同情况下正确使用 Pino JS 添加动态上下文,并且我一直在查看文档,并发现了一些我需要澄清的事情。
pino 记录器可以配置为both、Formattter 和Serializer,并且描述相似,但我想它们对每个都有特定的用途和意图。这是我在文档中找到的内容:
Object
)包含用于格式化日志行形状的函数的对象。这些函数应该返回一个可 JSON 的对象,并且永远不应该抛出异常。这些函数允许完全自定义生成的日志行。例如,它们可用于更改级别键名称或丰富默认元数据。
Object
)默认:
{err: pino.stdSerializers.err}
包含用于自定义对象序列化的函数的对象。这些函数应该返回一个可 JSON 的对象,并且永远不应该抛出异常。记录对象时,与序列化器的确切键匹配的每个顶级属性将使用定义的序列化器进行序列化。
当记录对象中的属性与序列化器中的属性匹配时,将应用序列化器。唯一的例外是 err 序列化器,因为它也适用于对象是 Error 实例的情况,例如logger.info(新错误('kaboom'))。请参阅 errorKey 选项来更改 err 命名空间。
我不清楚为什么我会使用其中一个而不是另一个,因为它们在功能上似乎非常相似。格式化程序do提到“默认元数据”作为示例,但这还不足以澄清...任何人都有一些见解,或者知道库如何不同地使用它们,有什么区别?
当记录的对象上存在数据时,序列化器仅修改要记录的数据子集。如果特定日志属性与序列化程序键匹配,则序列化程序将修改该数据子集。如果不是,则跳过。 格式化程序始终会被处理,并处理整个日志数据对象、绑定对象或级别对象(如文档中所定义)。
虽然您可以通过修改日志对象来完成格式化程序中序列化程序的工作,但序列化程序允许更细粒度的控制和逻辑分离,并且仅在要修改的数据存在时运行。这使得序列化器成为一种更灵活的方法,可以使用小型集中函数而不是单个数据处理器来修改日志数据。随着应用程序的复杂性规模扩大,这一点的重要性也随之增加。
这是我为了强调差异而编写的一个简单示例。
const pino = require("pino")
let loggerDefault = pino({}, "./log.log")
let loggerFormatter = pino({
formatters: {
log (data) {
console.log("formatters ", data)
return {
...data,
timeString: new Date().toISOString(),
timeData: {
ms: Date.now(),
sec: Date.now() / 1000
}
}
}
},
}, "./log.log")
let loggerSerializer = pino({
serializers: {
timeData (data) {
console.log("serializer ", data)
return {
ms: Date.now(),
sec: Date.now() / 1000
}
}
}
}, "./log.log")
let logNoTimeData = {
msg: "Log With No Time Data",
timeString: "-",
id: "myId",
}
let logWithTimeData = {
msg: "Log With Time Data",
timeString: "-",
timeData: {
ms: 0,
sec: 0
},
id: "myId",
}
loggerDefault.info(logNoTimeData)
loggerFormatter.info(logNoTimeData)
loggerSerializer.info(logNoTimeData)
loggerDefault.info(logWithTimeData)
loggerFormatter.info(logWithTimeData)
loggerSerializer.info(logWithTimeData)
在节点中运行此代码,并注意使用格式化程序进行日志记录如何修改与序列化程序相同的数据,但每次都会运行,但序列化程序仅运行第二次。可以看到控制台输出:
formatters { msg: 'Log With No Time Data', timeString: '-', id: 'myId' }
formatters {
msg: 'Log With Time Data',
timeString: '-',
timeData: { ms: 0, sec: 0 },
id: 'myId'
}
serializer { ms: 0, sec: 0 }
显示格式化程序在两个对象上运行,但序列化程序仅运行一次。将其与记录的输出进行比较(带注释):
default:
{"level":30,"time":1704959633972,"msg":"Log With No Time Data","timeString":"-","id":"myId"}
with formatter:
{"level":30,"time":1704959633972,"msg":"Log With No Time Data","timeString":"2024-01-11T07:53:53.976Z","id":"myId","modifiedBy":"formatter","timeData":{"ms":1704959633976,"sec":1704959633.976}}
with serializer:
{"level":30,"time":1704959633977,"msg":"Log With No Time Data","timeString":"-","id":"myId"}
default:
{"level":30,"time":1704959633977,"msg":"Log With Time Data","timeString":"-","timeData":{"ms":0,"sec":0},"id":"myId"}
with formatter:
{"level":30,"time":1704959633977,"msg":"Log With Time Data","timeString":"2024-01-11T07:53:53.977Z","timeData":{"ms":1704959633977,"sec":1704959633.977},"id":"myId","modifiedBy":"formatter"}
with serializer:
{"level":30,"time":1704959633977,"msg":"Log With Time Data","timeString":"-","timeData":{"modifiedBy":"serializer","ms":1704959633977,"sec":1704959633.977},"id":"myId"}
总结一下:
当您想要修改每个日志对象时,请使用格式化程序。 当您想要修改特定日志数据(如果存在)时,请使用序列化器。