处理器和 Middleware
@loggerjs/processors 是管线中间层的 runtime-neutral 工具箱。这里的所有能力都是同步、有序、错误隔离的:抛错的 processor 会被报告到 logger meta,管线继续运行。
存在两种形态(完整模型见 核心概念):
- Middleware 运行在
LogRecord上,在 id/message/error 工作之前执行,是 enrich 或 drop 的最低成本位置。 - Processors 运行在
LogEvent上,在投影之后执行;当你需要解析后的 event 形状时才必须使用。
配置任何 processor 都会关闭该 logger 的 record 快速路径;middleware 不会。
Runtime 支持
全部 27 个 processor 和 middleware 都支持浏览器/前端以及 Node.js/服务端运行时。这个包本身不依赖 DOM、IndexedDB、文件系统、流、worker 线程或任何厂商 SDK。
| Runtime | 支持 | 说明 |
|---|---|---|
| 浏览器 / 前端 | 支持 | 在数据离开页面之前,用于补充字段、隐私清洗、采样、去重、路由、breadcrumbs、结构校验,以及处理浏览器采集到的错误。 |
| Node.js / 服务端 | 支持 | 与 Node 的 transport 和 integration 搭配使用同一套 processor;堆栈解析和错误规范化适用于标准的 JavaScript 错误。 |
| Worker / edge / 库 | 支持 | 自定义 provider 函数必须保持同步。路由和 fingers-crossed 的目标必须是该运行时中可用的 transport。 |
Enrichment
| Export | 功能 |
|---|---|
tagsProcessor(tags) / tagsMiddleware(tags) | 把固定 tags 合并到每条日志。 |
typeProcessor(type) / typeMiddleware(type) | 设置 event type。 |
contextProcessor(ctx) / contextMiddleware(ctx) | 合并固定 context 字段。 |
enrichProcessor(input) / enrichMiddleware(input) | 通用 patch:传入静态 patch 或返回 { message, type, tags, data, context, trace, source } 的函数;返回 false 可丢弃。 |
traceContextProcessor(provider) / traceContextMiddleware(provider) | 每条日志都从 provider function 附加 { traceId, spanId, ... }。 |
隐私和标准化
| Export | 功能 |
|---|---|
redactProcessor(options) | 按键名、精确的点路径或正则,在 data/context/tags/结构化错误中遮盖或移除值。censor 是 replacement 的兼容别名。采用写时复制,异步 transport 不会看到只脱敏了一半的对象。 |
privacyGuardProcessor(options) | 广谱 PII 清洗,带内置 patterns(email、bearer token、类似银行卡号的数字)和自定义 patterns。 带有 toJSON() 的值(例如 URL)会按其序列化后的形态扫描。 |
normalizeErrorProcessor(options) | 强制 error shape:stack 截断、cause-chain 深度限制、可枚举属性捕获。 |
stackParserProcessor(options) / parseStack(stack) | 把堆栈解析为结构化的帧(文件、行、列、函数)。 |
schemaDevCheckProcessor(options) | 开发期的 event 结构校验,用于发现类型化事件与实际 payload 之间的偏差。 |
Redaction 行为
redactProcessor() 有意保持同步、解释执行。LoggerJS 不会用 eval 或 new Function 编译用户提供的 paths;自定义 matchers 是在 processor 错误边界内运行的普通函数。
选项:
keys:不区分大小写的键名、匹配键或完整路径的正则表达式,或自定义的(key, path, value) => boolean匹配函数。paths:相对于每个被脱敏 event field 的精确 dot paths,例如user.password或request.headers.authorization;这些不是 glob patterns。replacement:匹配时写入的值;默认"[REDACTED]"。censor:兼容 Pino 的replacement别名;设置了replacement时忽略。remove:直接省略匹配的对象属性,而不是替换。深度截断不属于键/路径匹配;到达maxDepth时,过深的子树仍会被折叠为replacement。maxDepth:最大遍历深度,默认8。到达深度上限时采取保守策略,直接替换整个子树,而不是输出未经检查的嵌套值。- 带有
toJSON()方法的值(例如URL、Buffer或自定义类)会按其序列化后的形态脱敏,因此URL仍保持为字符串。如果toJSON()抛错,该值会被替换。Date值保持原样。
成本与遍历对象大小乘以 matcher 数量成正比。热日志器优先使用精确 keys 和 paths;广泛 regex 和深层遍历适合 edge loggers 或低频错误路径。
流量控制
| Export | 功能 |
|---|---|
sampleProcessor(options) | 按 level rate 做概率采样;默认保留 error/fatal。 |
dynamicSamplerProcessor(options) | 按 category 在滑动窗口内自适应采样;抑制吵闹 logger,保留安静 logger。 |
rateLimitProcessor(options) | 按 category 的 token bucket;默认豁免 error/fatal。 |
dedupeProcessor(options) | 在时间窗口内把重复的相同日志折叠成一条带 count 的 event。 |
coalesceProcessor(options) | 抑制窗口内重复 events,并在下一个匹配 event 上输出前一次 repeat count。 |
fingerprintProcessor(options) | 从可配置 parts(logger、error.name、stack.top、自定义函数)计算稳定 fingerprint,用于 grouping 和 dedupe keys。 |
filterProcessor(input) | 通过判断函数或声明式规则(minLevel、logger、type、tags、integration 来源等)决定保留或丢弃。 |
levelOverrideProcessor(input) | 按 category pattern 提升或限制 levels,例如把吵闹依赖降级。 |
缓冲和路由
| Export | 功能 |
|---|---|
fingersCrossedProcessor(options) | 在每个 key 的 ring buffer 中保留低级别日志;触发级别出现时,把缓冲历史 flush 到目标 transport。典型用途是“只有出问题时才给我 debug logs”。 |
breadcrumbBufferProcessor(options) | 维护有上限的 breadcrumb 轨迹,并在触发条件的 event 上附加或重放它们。 |
routeProcessor(input) | 按规则把 event 固定投递到指定名称的 transport,或排除某些 transport(例如 [{ minLevel: "error", transports: ["alerts"] }])。 |
symbolicateStackProcessor(options) | 把 source map 或发布服务提供的符号化能力接入解析后的堆栈帧,但不内置 source map 解析器。 |
顺序建议
顺序很重要,每个阶段看到的是前一阶段输出:
- 先 enrich(tags、context、trace),让后续阶段能匹配这些字段。
- 再规范化(错误、堆栈),然后再做依赖错误结构的指纹计算或匹配。
- 在检查 data 的采样决策之前先 redact,并且始终在任何内容离开进程前 redact。
- 最后做流量控制(sample、rate-limit、dedupe),这样丢弃的是完整 events,counters 的含义也清楚。
ts
createLogger({
middleware: [tagsMiddleware({ service: "checkout" })],
processors: [
normalizeErrorProcessor(),
redactProcessor({ keys: ["password", /token/i] }),
sampleProcessor({ rates: { debug: 0.1 } }),
],
});编写自己的能力
Middleware:
ts
import { createMiddleware } from "@loggerjs/core";
const requestIdMiddleware = createMiddleware("request-id", (record) => {
record.props = { ...record.props, requestId: currentRequestId() };
return record; // 或 null 表示丢弃
});Processor:
ts
import type { Processor } from "@loggerjs/core";
const dropHealthChecks: Processor = (event) => {
if (event.data && (event.data as { path?: string }).path === "/healthz") return false;
return event;
};合约提醒:
- 只能同步执行,管线内不要
await。 - 替换共享对象,不要原地修改(
record.tags、record.ctx可能被冻结并共享)。 - 抛错会被报告并跳过;不要依赖某个 processor 一定运行成功。
