🚀 基础创建与级别 >>>
先确定实例创建、日志级别和对象日志写法,再扩展 transport 与上下文。
import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL ?? 'info'
})
logger.info('service started')
logger.error({ err: new Error('boom') }, 'request failed')
logger.level = 'debug'
if (logger.isLevelEnabled('trace')) {
logger.trace({ feature: 'search' }, 'trace message')
}
const auditLogger = pino({
customLevels: {
audit: 35,
security: 45
}
})
auditLogger.audit({ actor: 'admin' }, 'permission changed')
🧩 字段格式化与序列化 >>>
用 `formatters`、`serializers`、字段键名配置对接日志平台字段约定。
const logger = pino({
messageKey: 'message',
errorKey: 'error',
nestedKey: 'payload',
formatters: {
level(label, number) {
return { level: number, severity: label }
},
bindings(bindings) {
return { pid: bindings.pid, host: bindings.hostname }
}
},
serializers: {
err: pino.stdSerializers.err
}
})
🧵 Child Logger 与上下文 >>>
通过 `child()` 绑定请求、模块、租户等上下文,避免每次手写重复字段。
const rootLogger = pino({ level: 'info' })
const requestLogger = rootLogger.child({
requestId: 'req-42',
module: 'billing'
})
requestLogger.info('request accepted')
console.log(requestLogger.bindings())
requestLogger.setBindings({
userId: 'u-1001'
})
requestLogger.warn({ retryable: true }, 'upstream timeout')
🔒 Redaction 脱敏 >>>
把口令、令牌、卡号等字段在输出前统一遮罩或直接移除。
const logger = pino({
redact: {
paths: [
'req.headers.authorization',
'user.password',
'payment.card.cvv',
'items[*].secret'
],
censor: '[REDACTED]'
}
})
const logger = pino({
redact: {
paths: ['token', 'profile.ssn'],
remove: true
}
})
🚚 Transport 与输出管道 >>>
生产环境优先用 `pino.transport()` 把格式化、分流、落盘放到 worker 线程。
const transport = pino.transport({
targets: [
{
level: 'info',
target: 'pino/file',
options: { destination: './logs/app.log', mkdir: true }
},
{
level: 'error',
target: 'pino/file',
options: { destination: './logs/error.log', mkdir: true }
}
]
})
const logger = pino(transport)
const transport = pino.transport({
pipeline: [
{ target: 'pino-pretty', options: { colorize: true } }
]
})
const logger = pino({ level: 'debug' }, transport)
💾 Destination 与 flush >>>
直接写文件时用 `destination()`,需要确保进程退出前适时 `flush()`。
const destination = pino.destination({
dest: './logs/runtime.log',
minLength: 4096,
sync: false
})
const logger = pino(destination)
logger.info('queued')
logger.flush()
🌐 Browser 与前端转发 >>>
浏览器模式适合统一前后端日志接口,或将 warn/error 转发到远端采集端。
const logger = pino({
browser: {
asObject: true,
transmit: {
level: 'warn',
send(level, logEvent) {
navigator.sendBeacon('/log', JSON.stringify({ level, logEvent }))
}
}
}
})
🌍 Web 框架接入 >>>
与 Fastify、`pino-http` 配合时,重点是请求日志、上下文继承和敏感字段脱敏。
import Fastify from 'fastify'
const app = Fastify({
logger: {
level: 'info',
redact: ['req.headers.authorization']
}
})
app.get('/health', async (request) => {
request.log.info('health check')
return { ok: true }
})
import express from 'express'
import pinoHttp from 'pino-http'
const app = express()
app.use(pinoHttp())
app.get('/', (req, res) => {
req.log.info({ route: '/' }, 'handled')
res.end('ok')
})
🧪 Diagnostics 与排障 >>>
诊断通道可以观测内部序列化阶段,适合调试格式化或性能问题。
import diagnosticsChannel from 'node:diagnostics_channel'
const start = diagnosticsChannel.channel('tracing:pino_asJson:start')
const end = diagnosticsChannel.channel('tracing:pino_asJson:end')
start.subscribe((message) => {
console.log('serialize start', message.arguments)
})
end.subscribe((message) => {
console.log('serialize end', message.result)
})
⚠️ 生产使用要点
JSON 原始日志保留给机器消费,开发态再接 `pino-pretty` 或专用 transport 做展示。
JSON.stringify()requestId userId servicepino-pretty- 退出前如果使用异步 destination 或 transport,要确认日志已 flush。