处理器和 Middleware
@loggerjs/processors 是管线中间层的 runtime-neutral 工具箱。这里的所有能力都是同步、有序、错误隔离的:抛错的 processor 会被报告到 logger meta,管线继续运行。
存在两种形态(完整模型见 核心概念):
- Middleware 运行在
LogRecord上,在 id/message/error 工作之前执行,是 enrich 或 drop 的最低成本位置。 - Processors 运行在
LogEvent上,在投影之后执行;当你需要解析后的 event 形状时才必须使用。
配置任何 processor 都会关闭该 logger 的 record fast path;middleware 不会。
Runtime 支持
全部 27 个 processors 和 middleware 都支持 browser/frontend 与 Node.js/server runtime。这个包本身不依赖 DOM、IndexedDB、filesystem、streams、worker threads 或任何 vendor SDK。
| Runtime | 支持 | 说明 |
|---|---|---|
| Browser / frontend | 支持 | 在数据离开页面前,用于 enrichment、隐私清洗、sampling、dedupe、routing、breadcrumbs、schema checks 和 browser-captured errors。 |
| Node.js / server | 支持 | 与 Node transports 和 integrations 使用同一套 processors;stack parsing 和 error normalization 处理标准 JavaScript errors。 |
| Workers / edge / libraries | 支持 | 自定义 provider functions 保持同步。Routing 和 fingers-crossed targets 必须引用该 runtime 可用的 transports。 |
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) | 按 key name、精确 dot path 或 regex,在 data/context/tags/structured errors 中 mask 或 remove 值。censor 是 replacement 的非破坏别名。采用 copy-on-write,async transports 不会看到半脱敏对象。 |
privacyGuardProcessor(options) | 广谱 PII 清洗,带内置 patterns(email、bearer token、类似银行卡号的数字)和自定义 patterns。 |
normalizeErrorProcessor(options) | 强制 error shape:stack 截断、cause-chain 深度限制、可枚举属性捕获。 |
stackParserProcessor(options) / parseStack(stack) | 把 stack 解析为结构化 frames(file、line、column、function)。 |
schemaDevCheckProcessor(options) | 开发期 event shape 校验;发现 typed events 与实际 payload 的漂移。 |
Redaction 行为
redactProcessor() 有意保持同步、解释执行。LoggerJS 不会用 eval 或 new Function 编译用户提供的 paths;自定义 matchers 是在 processor 错误边界内运行的普通函数。
选项:
keys:大小写不敏感 key names、匹配 key 或完整 path 的 regexes,或自定义(key, path, value) => booleanmatcher。paths:相对于每个被脱敏 event field 的精确 dot paths,例如user.password或request.headers.authorization;这些不是 glob patterns。replacement:匹配时写入的值;默认"[REDACTED]"。censor:兼容 Pino 的replacement别名;设置了replacement时忽略。remove:省略匹配的对象属性,而不是替换。深度限制截断不是 key/path match;到达maxDepth时,过深 subtree 仍然会折叠为replacement。maxDepth:最大遍历深度;默认8。到达深度时 fail closed,替换整个 subtree,而不是输出未知嵌套值。
成本与遍历对象大小乘以 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) | 通过 predicate 或声明式规则(minLevel、logger、type、tags、integration source 等)keep/drop。 |
levelOverrideProcessor(input) | 按 category pattern 提升或限制 levels,例如把吵闹依赖降级。 |
缓冲和路由
| Export | 功能 |
|---|---|
fingersCrossedProcessor(options) | 在每个 key 的 ring buffer 中保留低级别日志;触发级别出现时,把缓冲历史 flush 到目标 transport。典型用途是“只有出问题时才给我 debug logs”。 |
breadcrumbBufferProcessor(options) | 维护有界 breadcrumb trail,并在触发 events 上附加或 replay。 |
routeProcessor(input) | 按规则把 events 固定到命名 transports 或排除 transports(例如 [{ minLevel: "error", transports: ["alerts"] }])。 |
symbolicateStackProcessor(options) | 把 source-map 或 release-service symbolication 接入 parsed stack frames,但不捆绑 source-map parser。 |
顺序建议
顺序很重要,每个阶段看到的是前一阶段输出:
- 先 enrich(tags、context、trace),让后续阶段能匹配这些字段。
- 再 normalize(errors、stacks),然后做依赖错误形状的 fingerprint 或 match。
- 在检查 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 一定运行成功。