# LoggerJS 中文完整 LLM Context
> 从中文站点页面生成的扩展文档上下文;API report 和源码链接保留英文原文,便于核对真实 export 和类型声明。
> 中文站包含主要指南的完整中文译文;生成参考页中的包名、export subpaths、TypeScript 声明和源码链接保留原文,便于与发布产物逐项核对。
# 中文首页
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/index.md
---
layout: home
hero:
name: LoggerJS
text: 更快、更强的同构日志库
tagline: 采集、处理、投递。一条高速管线。
image:
src: /logo.svg
alt: LoggerJS logo
actions:
- theme: brand
text: 快速开始
link: /zh/GETTING-STARTED
- theme: alt
text: 查看包
link: /zh/reference/packages
- theme: alt
text: API 参考
link: /zh/reference/api/
features:
- title: 浏览器和服务端
details: 同一 logger 模型运行在浏览器、Node.js、workers 和 edge runtimes。
- title: 自动采集
details: 按需捕获 console、错误、网络失败、路由、process 事件、HTTP framework、队列和数据库边界。
- title: 可靠投递
details: transport 拥有 codec,并可组合 batching、retry、backoff、offline replay、crash-path flush 和 beacon delivery。
- title: 组合式处理
details: middleware 和 processor 在投递前完成 enrich、redact、sample、dedupe、fingerprint、route 和 buffer。
- title: 可度量热路径
details: benchmark 和 CI gate 覆盖 disabled levels、lean NDJSON、prepared encoders、batching、browser delivery 和 size budgets。
- title: 对库友好的默认值
details: library logger 在宿主应用配置前保持静默,依赖可以记录日志但不强制输出。
---
> [!NOTE]
> 中文站包含主要指南的完整中文译文;生成参考页中的包名、export subpaths、TypeScript 声明和源码链接保留原文,便于与发布产物逐项核对。
## LoggerJS 管线
采集手写日志,加上浏览器和 Node integration。
塑形middleware 保持 raw record 低成本且可组合。
处理需要更丰富行为时,processor 再投影 event。
投递transport 选择 codec、batching、retry 和目的地。
```ts
import { createLogger, stdoutTransport } from "@loggerjs/node";
import { redactProcessor } from "@loggerjs/processors";
const logger = createLogger({
category: ["api"],
level: "info",
processors: [redactProcessor({ keys: ["password", /token/i] })],
transports: [stdoutTransport()],
});
logger.info("order created", { orderId: "ord_123" });
await logger.flush();
```
## 从哪里开始
新项目
先看 Node 或浏览器 quick start,再按运行时补 processor 和 transport。
生产上线
生产配方和运维指南覆盖隐私、离线队列、崩溃路径和 vendor 投递。
API 查询
生成的包和 API 页面用于确认 exports、subpaths 和公共声明。
## 文档地图
- [快速开始](/zh/GETTING-STARTED) 覆盖安装、第一个 logger、级别、lazy message、context 和 typed events。
- [核心概念](/zh/CONCEPTS) 解释 records、events、middleware、processors、transports、codecs、integrations 和 routing。
- [传输](/zh/TRANSPORTS)、[集成](/zh/INTEGRATIONS)、[处理器](/zh/PROCESSORS) 和 [编解码](/zh/CODECS) 是主要实现参考。
- [生产配方](/zh/PRODUCTION-RECIPES)、[运维](/zh/OPERATIONS) 和 [性能](/zh/PERFORMANCE) 帮助做上线选择。
- [基准](/zh/BENCHMARKS)、[基准矩阵](/zh/BENCHMARK-MATRIX) 和 [对比](/zh/COMPARISON) 把性能与定位绑定到仓库证据。
- [包](/zh/reference/packages)、[API 报告](/zh/reference/api/) 和 [示例](/zh/examples) 由当前仓库生成。
- [AI Skill](/zh/AI-SKILL)、[llms.txt](/zh/llms.txt) 和 [llms-full.txt](/zh/llms-full.txt) 帮助 coding agent 直接使用 LoggerJS。
---
# 快速开始
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/GETTING-STARTED.md
---
title: "快速开始"
description: "安装 LoggerJS,并创建第一个 Node.js 或浏览器 logger。"
---
# 快速开始
LoggerJS 是一个同构的结构化日志 SDK。同一套 core API 可以运行在 Node、浏览器、worker 和 edge runtime 中;平台包在 core 之上增加 transport 和自动采集能力。
## 安装
按运行平台选择包。每个平台包都会重新导出 `@loggerjs/core` 的全部内容,所以安装一个平台包就足以开始使用。
```bash
# Node services
pnpm add @loggerjs/node @loggerjs/processors
# Browser apps
pnpm add @loggerjs/browser @loggerjs/processors
```
所有包都提供 ESM 和 CJS 入口,并带完整 TypeScript 声明。对 Node 消费者,发布 tarball 会在 Node 20.19.0、22 和 24 上做 smoke test。仓库开发使用 Node >=22.13.0 来运行完整工具链。
## 第一个 Logger(Node)
```ts
import { captureProcessIntegration, createLogger, stdoutTransport } from "@loggerjs/node";
import { redactProcessor } from "@loggerjs/processors";
const logger = createLogger({
category: ["api"],
level: "info",
tags: { service: "checkout", env: process.env.NODE_ENV ?? "dev" },
processors: [redactProcessor()],
transports: [stdoutTransport()],
integrations: [captureProcessIntegration()],
});
logger.info("order created", { orderId: "ord_123" });
logger.error("payment failed", new Error("card declined"));
await logger.flush();
```
`stdoutTransport()` 每条日志写出一行 NDJSON。`captureProcessIntegration()` 会自动把 uncaught exception、unhandled rejection 和 process warning 转成日志事件。
## 第一个 Logger(Browser)
```ts
import {
browserHttpTransport,
captureBrowserErrorsIntegration,
captureConsoleIntegration,
createLogger,
memoryBrowserHttpOfflineQueue,
pageLifecycleIntegration,
} from "@loggerjs/browser";
const logger = createLogger({
category: ["web"],
level: "info",
transports: [
browserHttpTransport({
url: "/api/logs",
offlineQueue: memoryBrowserHttpOfflineQueue({ maxEntries: 500 }),
useBeaconOnPageHide: true,
}),
],
integrations: [
captureConsoleIntegration({ levels: ["warn", "error"] }),
captureBrowserErrorsIntegration(),
pageLifecycleIntegration(),
],
});
logger.info("page loaded");
```
HTTP transport 会批量发送日志;离线时写入队列;浏览器恢复 `online` 后重放;页面关闭时回退到 `navigator.sendBeacon` 做最后机会投递。
## 级别
六个启用级别,加上 `silent`:
| 名称 | 数值 |
| --- | ---: |
| `trace` | 10 |
| `debug` | 20 |
| `info` | 30 |
| `warn` | 40 |
| `error` | 50 |
| `fatal` | 60 |
```ts
logger.setLevel("debug");
logger.isLevelEnabled("trace"); // false
```
禁用级别只付出一次数字比较成本:不分配对象、不查上下文、不格式化消息。
## 惰性消息
当构造消息很贵时,传入函数。只有级别启用时才会调用,并且最多调用一次:
```ts
logger.debug(() => `cart state: ${JSON.stringify(cart)}`);
```
## 错误
第一个参数是 `Error` 时,它会成为 record 的 error;也可以显式给一条消息:
```ts
logger.error(err);
logger.error(err, "payment failed", { orderId: "ord_123" });
```
transport 看到错误之前,LoggerJS 会先把错误标准化为 name、message、截断 stack、可枚举属性和 cause 链。
## 子 Logger 和 Tags
```ts
const checkoutLogger = logger.child({
category: ["api", "checkout"],
tags: { domain: "checkout" },
});
```
子 logger 继承 level、tags、bindings、middleware、processors 和 transports;integrations 不会继承。`withTags()` 和 `withType()` 是常见子 logger 形态的快捷方式。
## 环境上下文
把 request 级别的值绑定一次,不需要每次日志调用都手动传:
```ts
import { withContext } from "@loggerjs/core";
import { installAsyncLocalStorageContext } from "@loggerjs/node";
installAsyncLocalStorageContext(); // 启动时调用一次
await withContext({ requestId: "req_123" }, async () => {
logger.info("request started"); // context: { requestId: "req_123" }
});
```
浏览器默认的栈式 context manager 覆盖同步作用域;Node 中的 AsyncLocalStorage 会让 context 跨 `await` 边界传播。
## 类型化事件
定义可复用、带类型的事件形状:
```ts
import { defineEvent } from "@loggerjs/core";
const CheckoutCompleted = defineEvent<{ orderId: string; amountCents: number }>({
type: "checkout.completed",
message: (event) => `checkout completed ${event.orderId}`,
tags: { domain: "checkout" },
});
logger.event(CheckoutCompleted, { orderId: "ord_123", amountCents: 4999 });
```
## 库作者:Registry
库代码不应该自己构造 logger;它按 category 查找 logger,并在宿主应用配置输出之前保持静默:
```ts
// In the library
import { getLogger } from "@loggerjs/core";
const logger = getLogger(["my-lib", "client"]);
logger.debug("handshake started"); // 配置前是 no-op
// In the application
import { configure } from "@loggerjs/core";
await configure({
transports: { stdout: stdoutTransport() },
loggers: [{ category: ["my-lib"], level: "warn", transports: ["stdout"] }],
});
```
## 关闭
```ts
await logger.flush(); // drain pending transport work
await logger.close(); // tear down integrations, close transports
```
崩溃路径中,支持同步刷新的 transport 会暴露 `flushSync()`;详见 [运维](OPERATIONS.md)。
## 下一步
- [核心概念](CONCEPTS.md):records、events、middleware、processors、transports、codecs 的管线模型。
- [传输](TRANSPORTS.md):所有内置 transport,以及如何编写自定义 transport。
- [友好输出](PRETTY.md):浏览器 DevTools 和 Node 终端的人类可读输出。
- [集成](INTEGRATIONS.md):浏览器和 Node 自动采集。
- [处理器](PROCESSORS.md):middleware/processor 工具箱。
- [编解码](CODECS.md):序列化归属和 codec 合约。
- [性能](PERFORMANCE.md):如何按吞吐量配置。
- [运维](OPERATIONS.md):隐私、离线队列和崩溃路径。
- [生产配方](PRODUCTION-RECIPES.md):浏览器 HTTP/offline、Node stdout+OTLP、Loki/Datadog 部署。
- [API 稳定性](API-STABILITY.md):v1 稳定 API 子集和 pre-1.0 兼容策略。
- [迁移](MIGRATION.md):从 pino、winston 或 console 迁移。
## 相关链接
- [英文原文](/GETTING-STARTED)
- [中文首页](/zh/)
---
# 核心概念
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/CONCEPTS.md
---
title: "核心概念"
description: "理解 record、event、middleware、processor、transport、codec 与 integration。"
---
# 核心概念
LoggerJS 围绕三个面向用户的概念组织:**integrations**、**middleware/processors** 和 **transports**。再加上一条边界规则:**codecs 属于 transports**。本页解释连接这些部分的管线。
## 管线
```text
logger.info("msg", data)
│
├─ level gate 一次数值比较;禁用级别到这里直接停止
│
├─ LogRecord built lazy message 暂不求值,保留原始 error,
│ 附加 category/type/tags/context
│
├─ middleware 同步、有序,可以修改或丢弃 record
│
├─ processors? 如果存在任何 processor,record 会投影为
│ │ LogEvent(分配 id、解析 message、标准化 error),
│ │ 然后在 event 上运行 processors
│ │
│ └─ no processors: record 直接进入 transports
│ (“record fast path”,没有投影成本)
│
└─ transports 每个 transport 接收 record(write/writeBatch)
│ 或 event(log/logBatch);转换只发生一次并复用
│
└─ codec transport 用自己的 codec 序列化
```
Integrations 位于这个流程外部:它们 hook 平台行为(console 调用、错误、fetch、process 事件),再通过 `api.capture()` 把捕获到的输入送进同一条管线。
## LogRecord 与 LogEvent
`LogRecord` 是热路径形状。它保留原始值,确保在真正需要之前不做工作:
- `lazy`:尚未求值的消息函数,最多解析一次。
- `err`:原始错误值,尚未标准化。
- `props`:用户数据对象;除非 middleware、processor 或 transport 显式 clone,否则按引用共享。
- `ctx`:冻结的绑定上下文对象,按引用共享。
- `tags`:可能是 logger 的冻结 tags 对象,按引用共享。
- 没有 `id`:id 计算延迟到 event 投影阶段。
`LogEvent` 是面向 transport 的兼容形状:`id`、`time`、`seq`、`level`、`levelName`、`logger`(点分 category)、`message`(解析后的字符串)、`type`、`tags`、`data`、`error`(标准化后的 `SerializedError`)、`context`、`trace`、`source`。
`recordToEvent()` / `eventToRecord()` 可以互相转换。转换存在文档化的信息损失:`runtime` source 会折叠为 integration source,标量 event data 会包装成 `{ value }`。对象 data 默认不会快照;如果后续修改不能影响延迟 transport,请在记录前 clone。
### 修改合约
Middleware 可以修改 record,但有一条规则:**替换字段,不要原地修改共享对象**。`record.ctx` 和 logger 级别的 `record.tags` 是冻结且跨 record 共享的;应写 `record.tags = { ...record.tags, extra }`,不要写 `record.tags.extra = ...`。原地修改冻结字段会抛错,并作为 middleware error 上报,不会污染其他 records。
## Middleware 与 Processors
两者都是同步、有序且错误隔离的。区别在于看到的数据和运行时机:
| | Middleware | Processor |
| --- | --- | --- |
| 输入 | `LogRecord` | `LogEvent` |
| 运行时机 | 在 id/message/error 工作之前 | 在投影之后 |
| 丢弃 | 返回 `null` | 返回 `false` |
| 修改 | 原地修改(替换字段) | 返回新 event |
| 丢弃成本 | 最低 | 已经付过投影成本 |
优先用 middleware 做 enrichment 和早期过滤。需要解析后的 event 形状时再用 processors,例如按 event 字段路由、对标准化错误做 fingerprint、为 fingers-crossed delivery 缓冲 events。
**只要配置了任何 processor,该 logger 的 record fast path 就会关闭**,因为每条日志都必须先投影为 event。当你需要 event 级别行为时,这是正确取舍;数字见 [性能](PERFORMANCE.md)。
## Transports
一个 transport 可以实现以下四类写入方法中的任意组合:
```ts
interface Transport {
name?: string;
minLevel?: LoggerLevel;
ready?(): void | Promise;
write?(record: LogRecord, context: TransportContext): void | Promise;
writeBatch?(
records: LogRecord[],
context: TransportContext,
): void | Promise;
log?(event: LogEvent, context: TransportContext): void | Promise;
logBatch?(
events: LogEvent[],
context: TransportContext,
): void | Promise;
flush?(): void | Promise;
flushSync?(): void;
close?(): void | Promise;
}
```
- 支持 record 的 transports(`write`/`writeBatch`)参与 fast path,并可以直接编码 records。
- Event transports(`log`/`logBatch`)接收投影后的 events。
- `context.toEvent(record)` 按需转换;结果按 record memoize,所以多个 transports 共享一次投影,id 在多次转换间保持稳定。
- transport 抛出的同步或异步错误都会被捕获并上报到 logger meta;一个失败 transport 不会阻塞其他 transport。
- `ready()` 是显式、可选的。普通日志调用不会等待 transport 启动;需要确认启动完成的调用方使用 `logger.ready()`。
- `close()` 必须在释放资源前包含自己的 best-effort flush。Core 在有 `close()` 时调用它;只有 transport 没有 `close()` 时才回退到 `flush()`。
## Codecs 属于 Transports
序列化由 transport 拥有,并通过自己的 codec 配置。Middleware 和 processors 保持原始值,不要在管线内部提前 stringify。这样结构化脱敏仍然有效,每个目的地可以选择自己的 wire format,batching 也可以摊薄序列化成本。
```ts
stdoutTransport({ codec: ndjsonCodec() });
browserHttpTransport({ url: "/api/logs", codec: fastEventJsonCodec() });
```
合约和 fast-by-default 安全语义见 [编解码](CODECS.md)。
## Integrations
Integration 是一个具名的 `setup(api)` 函数,负责 hook 平台表面并返回 teardown:
```ts
interface Integration {
name: string;
setup(api: IntegrationSetupContext): void | Teardown;
}
```
setup context 提供 logging API 和三个安全工具:
- `api.capture(input)`:把捕获信号送入管线,并标记 `source: "integration:"`。
- `api.guard(fn)`:重入保护。如果 patched 代码路径调用 logger,而 logger 又调用到 patched 代码,内层调用会被丢弃并计数,避免无限循环。
- `api.unpatched`:原始函数注册表(`console.*`、`fetch`、`XMLHttpRequest`),让 transports 和 integrations 在 patch 存在时仍能调用真实实现。
Integrations 会在 logger 构造时或 `addIntegration()` 时安装;每个 integration instance 只 setup 一次,并在 `close()` 时按反向顺序 teardown。
## 路由
Processors 可以把 event 固定到命名 transports:
```ts
import { routeProcessor } from "@loggerjs/processors";
routeProcessor([{ minLevel: "error", transports: ["alerts"] }]);
```
Routes 作为不可枚举 event metadata 附加,并在 dispatch 时使用。Record fast path 不执行 route 过滤:routes 只能由 processors 附加,而 record path 只会在 logger 没有 processor 时运行。
## Levels、Categories、Sources
- Levels 是数字(`trace` 10 到 `fatal` 60)并带名称;自定义数字级别在各处可用。
- Categories 是字符串数组(`["api", "checkout"]`),在 events 中 join 成点分 logger 名;registry 按 category prefix 路由配置。
- `source` 区分应用日志和 integration 捕获日志,因此 console capture 可以从 console output 中排除,也能检测循环。
## 内部错误和 Meta Counters
管线永远不会把错误抛进应用代码。Middleware、processors、codecs 和 transports 中的失败会通过 `onInternalError` 上报,并计入 logger meta:
```ts
import { getLoggerMetaStats } from "@loggerjs/core";
getLoggerMetaStats();
// { "transport.errors": 1, "transport.dropped.queue-full": 2, "codec.fallback": 1, ... }
```
用这些 counters 监控静默退化:队列丢弃、codec fallback、integration 重入丢弃等。`getLoggerSelfMetrics()` 同时返回 counters 和 gauges,包括共享 transport helpers 暴露的 queue depth 和 circuit-breaker 状态。
## Trace 和语义事件
`trace-propagation` helpers 负责解析/格式化 W3C `traceparent` 和 baggage headers;`addContextProvider()` 允许 integrations 附加 ambient context,而不替换应用自己的 context provider。`semanticEvents` 定义常见事件家族(`error`、`http`、`db`、`job`、`ui`、`action`、`security`、`performance`),让 integrations 和应用日志共享字段命名。
## 延伸阅读
- [架构](ARCHITECTURE.md):完整设计文档、invariants 和决策记录。
- [传输](TRANSPORTS.md)、[集成](INTEGRATIONS.md)、[处理器](PROCESSORS.md)、[编解码](CODECS.md):参考目录。
## 相关链接
- [英文原文](/CONCEPTS)
- [中文首页](/zh/)
---
# 传输
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/TRANSPORTS.md
---
title: "传输"
description: "内置 transport、可靠性边界、批量/重试/降级以及自定义 transport。"
---
# 传输
Transport 负责把日志 records 或 events 投递到某个目的地。本页列出所有内置 transport,并说明如何编写自己的 transport。精确 option types 以各 package 的 TypeScript declarations 和 `api-reports/` 为准。
每个 transport 到源码文件、公开入口和契约测试的可审计映射,见 [传输契约矩阵](TRANSPORT-CONTRACTS.md)。
## Runtime 支持
| Runtime | Transport support | 说明 |
| --- | --- | --- |
| Core / runtime-neutral | `consoleTransport`, `memoryTransport`, `testTransport`, `batchTransport`, `retryTransport`, `fallbackTransport` | 不依赖浏览器或 Node.js 专属 API。Wrappers 可以包住当前 runtime 可用的任何 transport。 |
| Pretty / developer UX | `prettyConsoleTransport`, `prettyStreamTransport`, `prettyStdoutTransport`, `prettyStderrTransport` | 来自 `@loggerjs/pretty` 的浏览器 DevTools 和 Node terminal 显示 transports。用于人类可读输出,不是 durable production delivery。 |
| Browser / frontend | `browserHttpTransport`, IndexedDB queues/store, WebSocket, service worker, BroadcastChannel, offline-first replay | 使用 `fetch`、`sendBeacon`、`IndexedDB`、`navigator.onLine`、service workers 和 BroadcastChannel 等浏览器 API;在可用处做 feature detection 和 fallback。 |
| Node.js / server | `stdoutTransport`, `stderrTransport`, `fileTransport`, `rotatingFileTransport`, `nodeHttpTransport`, `nodeSyslogTransport`, `workerTransport` | 使用 Node.js streams、filesystem、worker threads、network sockets 和 Node fetch。 |
| Vendor / observability | OTLP, Sentry, Datadog, Elastic, Loki, CloudWatch | HTTP wire transports 在具备 `fetch`/crypto/runtime 要求的环境中运行;SDK/provider adapters 需要应用传入已初始化的 SDK object 或 provider。Vendor credentials 通常更适合放在服务器或可信 worker。 |
| Database / local app / backend | `databaseTransport`, `postgresTransport`, `sqliteTransport` | LoggerJS 层是 driver-agnostic,但应用必须提供数据库 drivers;面向 Node.js、Electron、CLIs 或 backend workers。 |
## 稳定性级别
Transport 稳定性描述 public API 承诺,不是绝对投递保证。浏览器存储、进程关闭、网络 collectors 和 vendor backends 仍可能失败;下方可靠性表才是投递契约。
| 级别 | 含义 |
| --- | --- |
| Stable | 计划用于 v1-compatible application use。Option names 和高层语义由 API reports、tests 和 docs 保护。 |
| Compatible | 公开且有测试,但精确 runtime behavior 或 message shape 在 v1 前仍可能调整。适用于 caveats 与部署匹配的场景。 |
| Experimental | 公开且有测试,但还不属于 v1 兼容承诺。Names、options、payload mapping 或 batching guidance 在 v1 前可能变化。 |
| Runtime-dependent | Public API 稳定,但实际可靠性高度依赖 LoggerJS 外部的浏览器、worker、storage、network、SDK 或 database 行为。必须在目标环境验证。 |
| Test-only | 为 assertions 和 fixtures 构建,不用于生产投递。 |
| Transport | Stability | 原因 |
| --- | --- | --- |
| `consoleTransport()` | Stable | Runtime-neutral local sink,并为 console capture 做 loop prevention。 |
| `memoryTransport()` | Stable | 有界 in-memory diagnostics cache;有意非 durable。 |
| `testTransport()` | Test-only | 带 wait/snapshot APIs 的 assertion helper。 |
| `batchTransport()` / `retryTransport()` / `fallbackTransport()` | Stable | 第一方 transports 使用的 core reliability wrappers。 |
| Pretty transports | Stable | Developer display API 稳定;具体颜色/布局属于表现细节。 |
| `stdoutTransport()` / `stderrTransport()` / `fileTransport()` | Stable | 带 drain 和 crash-path 行为的生产本地 sinks。 |
| `rotatingFileTransport()` | Stable | 本地 size rotation;每个文件使用一个 writer process。 |
| `nodeHttpTransport()` | Stable | 自带 batch 包装的 HTTP delivery,共享 reliability options。 |
| `otlpHttpTransport()` | Experimental | OTLP mapping 公开且有测试,但 observability adapter packages 在 v1 前不冻结。 |
| `nodeSyslogTransport()` | Stable | Wire formatting 稳定;UDP/TCP 可靠性遵循 syslog transport 语义。 |
| `workerTransport()` | Compatible | Message protocol 公开,但 ready/ack/fallback lifecycle tuning 可能继续演进。 |
| `browserHttpTransport()` | Stable | 主要 browser remote transport;pagehide beacon 仍是 best effort。 |
| `memoryBrowserHttpOfflineQueue()` | Stable | 临时离线期 API 稳定;不具备 reload durability。 |
| `indexedDbBrowserHttpOfflineQueue()` / `indexedDbTransport()` / `offlineFirstTransport()` | Runtime-dependent | API 稳定,但持久性依赖浏览器 IndexedDB、quota、eviction、private mode 和 storage policy。 |
| `browserWebSocketTransport()` | Compatible | 适合 live/debug channels;reconnection 和最终 durability 由调用方负责。 |
| `browserServiceWorkerTransport()` | Runtime-dependent | API 公开,但 delivery 依赖 service worker registration、activation 和 lifetime。 |
| `browserBroadcastChannelTransport()` | Compatible | 同源 tab fan-out 有意是 lossy 且 receiver-dependent。 |
| Datadog / Elastic / Loki / CloudWatch transports | Experimental | Wire payloads 有测试,但 vendor packages 在 v1 前不冻结;生产 durability 需要在 raw transports 外包 batching/retry。 |
| `sentryTransport()` / `openTelemetryLogBridgeTransport()` | Experimental | Adapter contracts 公开且有测试,但 SDK/provider mapping 在 v1 前仍可能变化。 |
| `databaseTransport()` / `sqliteTransport()` / `postgresTransport()` | Experimental | Adapter APIs 公开且有测试,但 driver transaction 和 schema expectations 还需要更多 design-partner 验证。 |
## Import Boundaries
Root package imports 是方便入口。Public transport subpaths 被明确记录,用户可以选择更窄的 bundle,新内置 transports 也不能在没有对应文档时悄悄扩大 surface。
| Runtime | Public transport subpaths |
| --- | --- |
| Core | `@loggerjs/core/transport-console`, `@loggerjs/core/transport-batch`, `@loggerjs/core/transport-reliability`, `@loggerjs/core/transport-test` |
| Browser | `@loggerjs/browser/transport-http`, `@loggerjs/browser/transport-broadcast-channel`, `@loggerjs/browser/transport-service-worker`, `@loggerjs/browser/transport-websocket`, `@loggerjs/browser/transport-indexeddb`, `@loggerjs/browser/offline-first-transport` |
| Node.js | `@loggerjs/node/transport-http`, `@loggerjs/node/transport-file`, `@loggerjs/node/transport-rotating-file`, `@loggerjs/node/transport-stdout`, `@loggerjs/node/transport-syslog`, `@loggerjs/node/transport-worker` |
| Pretty | `@loggerjs/pretty/transport-console`, `@loggerjs/pretty/transport-stream` |
| Observability and data | `@loggerjs/otel/transport-http`, `@loggerjs/sentry/transport`, `@loggerjs/datadog/transport`, `@loggerjs/elastic/transport`, `@loggerjs/loki/transport`, `@loggerjs/cloudwatch/transport`, `@loggerjs/database/transport` |
当 public transport subpath 被导出但未列在这里时,`pnpm verify:component-docs` 会失败。新增 entries 也应该更新上方稳定性和可靠性表。
## 可靠性姿态
Transports 默认可组合。有些 transports 内部包含 batching 或 durable local storage;raw vendor wire transports 除非被包装,否则不会 retry。把下表视为生产投递契约:
| Transport 或 wrapper | 默认姿态 | 生产说明 |
| --- | --- | --- |
| `consoleTransport()` | 立即本地写入 | 人类/开发输出;除 console target 自身外没有 retry 或 durability。 |
| `prettyConsoleTransport()` / `prettyStdoutTransport()` / `prettyStderrTransport()` | 立即写出人类可读本地输出 | 只用于开发体验。生产投递使用结构化 transports。 |
| `memoryTransport()` | in-memory ring buffer | 仅 diagnostics cache;进程/页面退出即丢失。 |
| `testTransport()` | in-memory assertion sink | 仅测试;不是生产投递机制。 |
| `batchTransport(inner)` | 带可选 retry/circuit breaker 的 batch queue | raw I/O transports 需要 queue bounds、retries、backoff 或 drop accounting 时使用。 |
| `retryTransport(inner)` | 立即投递加 retry | inner transport 已拥有 batching,或 per-call retry 可接受时使用。 |
| `fallbackTransport(primary, fallback)` | primary 失败后走 fallback | 用作本地 backup sinks,不替代 queueing。 |
| `stdoutTransport()` / `stderrTransport()` | 立即 stream write,`flush()` 感知 drain,可选 `minLength` buffering | 本地 process sink;默认把 `EPIPE` 当成干净 shutdown。 |
| `fileTransport()` | 共享 file destination,支持 async stream mode、可选 `sync: true`、`mkdir`、`append`、`minLength` 和 crash-path `flushSync()` | 本地 durability path;每个文件优先一个 writer process。 |
| `rotatingFileTransport()` | 带 size rotation 的同步共享 file destination | 带大小轮转的本地 durability path;写入时阻塞调用方。 |
| `nodeHttpTransport()` | 自带 batch 包装的 HTTP delivery | 使用 `batchTransport`;生产中调节 queue、retry 和 circuit options。 |
| `nodeSyslogTransport()` | 立即 UDP/TCP syslog write | UDP 可丢;TCP 仍依赖 socket state 和 close/flush 行为。 |
| `workerTransport()` | worker offload,可选 ready/ack lifecycle | 默认 fire-and-forget;需要观察 worker acceptance 时配置 `readyTimeoutMs`、`ackTimeoutMs`、fallback 和 `autoEnd`;配置 ready handshake 后,`ready()` 等待 worker startup。 |
| `browserHttpTransport()` | batched fetch,可选 offline queue 和 beacon pagehide mode | reload survival 需要 IndexedDB queue;beacon mode 是 best-effort 且有大小限制。 |
| `memoryBrowserHttpOfflineQueue()` | in-memory offline queue | 可跨网络短暂中断,不跨 reload 或 tab close。 |
| `indexedDbBrowserHttpOfflineQueue()` | IndexedDB offline queue | quota/storage 可用时跨 reload。 |
| `offlineFirstTransport(remote)` | remote delivery 加 persistent queue replay | offline 或 remote failure 时 queue,之后 replay。 |
| `indexedDbTransport()` | 本地 IndexedDB persistence | 本地 support/export store;durability 依赖浏览器 storage policy 和 quota。 |
| `browserWebSocketTransport()` | socket closed 时 queue | Reconnection 由调用方负责;有界队列满时会 drop。 |
| `browserServiceWorkerTransport()` | queue 到 active service worker 可用;`target: "ready"` 时 `ready()` 可等待 `serviceWorker.ready` | Delivery 依赖 registration、activation 和 worker lifetime。 |
| `browserBroadcastChannelTransport()` | lossy tab broadcast | Receivers 必须已经监听;不 durable。 |
| `otlpHttpTransport()` | 自带 batch 包装的 OTLP/HTTP delivery | 使用 `batchTransport`;生产中调节 retry 和 circuit options。 |
| Datadog / Elastic / Loki / CloudWatch transports | raw HTTP wire delivery | 用 `batchTransport()` / `retryTransport()` 包装以获得 queueing、retry 和 circuit breaking。 |
| `sentryTransport()` / `openTelemetryLogBridgeTransport()` | SDK/provider adapter | 可靠性取决于传入的 SDK/provider。 |
| `databaseTransport()` / `sqliteTransport()` / `postgresTransport()` | batched database writes | 实际 transaction 和 connection 行为由 adapter/driver 拥有。 |
## Core / Runtime-Neutral(`@loggerjs/core`)
| Transport | 功能 |
| --- | --- |
| `consoleTransport()` | 按 level 的 pretty console output,或 `pretty: false` 时单行 JSON。通过 unpatched console 写出,避免 console capture 循环。默认过滤从 console 捕获来的 events。 |
| `memoryTransport()` | 最近 events 的 ring buffer(`maxEvents`,默认 1000)。适合 diagnostics endpoints 和 tests。 |
| `testTransport()` | 面向 assertion 的 sink:snapshots、call stats、`waitForEvent()`/`waitForCount()`、可注入失败。 |
| `batchTransport(inner, options)` | 为任意 transport 加 batching、retry 和 reliability controls。 |
| `retryTransport(inner, options)` | 为任意 transport 加 retries、exponential backoff、可选 circuit breaker 和可选 fallback。 |
| `fallbackTransport(primary, fallback)` | primary 抛错时发送到 fallback transport。 |
### `batchTransport` 可靠性选项
生态中的每个 batch-based transport 都共享这组选项:
```ts
batchTransport(inner, {
maxRecords: 100, // 队列达到这个数量时 flush
maxBytes: 64 * 1024, // 每 batch 字节预算(仅设置时估算)
maxWaitMs: 2000, // flush timer
maxQueueSize: 1000, // backpressure bound
dropPolicy: "drop-oldest" /* | "drop-newest" | "throw" */,
concurrency: 2, // 并行 in-flight batches
maxRetries: 3,
retryBaseDelayMs: 250, // exponential backoff base
retryMaxDelayMs: 5000,
circuitBreakerFailureThreshold: 5,
circuitBreakerResetMs: 30000,
onDrop: (event, reason) => metrics.increment(`log_drop.${reason}`),
});
```
注意:
- Byte estimation 会遍历 payload;只有 `maxBytes` 有限时才执行。
- Drops 总会计入 logger meta(`transport.dropped.*`);只有注册了 listener 时才进行 `onDrop` 的 event conversion。
- 失败 batch 会重新入队到队头;circuit breaker 避免持续打爆死亡 endpoint。
## Pretty / Developer UX(`@loggerjs/pretty`)
| Transport / helper | 功能 |
| --- | --- |
| `prettyConsoleTransport()` | 浏览器 DevTools 和本地 console 输出:level labels、可读 details、可选 `%c` 浏览器样式、原始对象参数、console-capture loop filtering。 |
| `prettyStreamTransport({ stream })` | 向任意 writable stream-like target 写入人类可读行。按配置或 auto-detect 使用 ANSI colors。 |
| `prettyStdoutTransport()` / `prettyStderrTransport()` | `process.stdout` / `process.stderr` 之上的 Node terminal helpers;尊重 `NO_COLOR` 和 `FORCE_COLOR`,支持 `minLevel`,`flush()` 可等待 `drain`。 |
| `formatPrettyEvent()` | 自定义显示 transports 的共享 formatter。返回 plain text、ANSI text、browser console args 和 raw details。 |
Pretty transports 是显示 sinks。它们不 batch、不 retry、不 persist,也不实现 collector protocols。示例和选项建议见 [友好输出](PRETTY.md)。
## Node.js / Server(`@loggerjs/node`)
| Transport | 功能 |
| --- | --- |
| `stdoutTransport()` / `stderrTransport()` | NDJSON lines,带 write backpressure tracking、clean `EPIPE` handling 和可选 `minLength` buffering;`flush()` 等待 pending writes。 |
| `fileTransport({ path })` | 默认 append NDJSON 到文件;支持 `mkdir`、`append: false`、async `minLength` buffering、`sync: true` 和 crash-path `flushSync()`。 |
| `rotatingFileTransport({ path, maxBytes, maxFiles })` | 基于大小的 rotation,通过同一个 file destination 生成 numbered archives。同步写入;每个文件使用一个 logger process。 |
| `nodeHttpTransport({ url })` | 基于 fetch 的 HTTP delivery,包在 `batchTransport` 中(Node 18+)。 |
| `nodeSyslogTransport()` | UDP/TCP 上的 RFC syslog formatting;`formatSyslogMessage()` 单独导出。 |
| `workerTransport({ workerScript })` | 用 codec 编码 batches 并 post 到 worker thread,可选 transfer buffers;支持 ready timeout、batch ack waiting、fallback 和 `autoEnd`。 |
`nodeHttpTransport()` 接收 `transformPayload`,可在 codec 后做 wire transform。gzip、brotli 或 deflate 使用 `nodeCompressionPayloadTransform()`:
```ts
import { nodeCompressionPayloadTransform, nodeHttpTransport } from "@loggerjs/node";
nodeHttpTransport({
url: "https://collector.example/logs",
transformPayload: nodeCompressionPayloadTransform({ format: "brotli" }),
});
```
`fileTransport().flushSync()` 是 crash-path primitive。在 async stream mode 下,它会通过同步 fd 写出当前 buffered 或 pending payloads,让 fatal records 在进程退出前到达磁盘;如果进程继续运行,原 async stream 可能仍会完成。普通 drain-and-continue shutdown 使用 `await flush()`;每次写入都必须同步时配置 `sync: true`。
`workerTransport()` 仍兼容只接收 object messages 的简单 workers。Lifecycle 是 opt-in:
- 当 worker 会发送 `{ type: "loggerjs:ready" }` 时,设置 `readyTimeoutMs`。超时会标记 worker 失败,并把 batch 发送到 fallback,或计为 `transport.dropped.worker-ready-timeout`。显式 `transport.ready()` / `logger.ready()` 也会等待这个 startup handshake。
- 当 worker 会用 `{ type: "loggerjs:batch:ack", id }` ack 每个 batch 时,设置 `ackTimeoutMs`。`flush()` 会等待这些 acks。
- 主线程 post `{ type: "loggerjs:batch", id?, codec, contentType, count, payload }`。
- Worker 可用 `{ type: "loggerjs:error", message, error }` 报告失败;pending batches 会 fallback 或计为 dropped。
- `autoEnd` 默认 `true`;如果 worker 被共享且不应由 transport `close()` terminate,设置 `autoEnd: false`。
Worker lifecycle 会更新标准 transport gauges:`transport.ready.` 和 `transport.queue.depth.`;pending ack failures 会计入 `transport.worker.pending-dropped` 和 `transport.dropped.`。
Node runtime diagnostics 可以从 `@loggerjs/node` 调用 `installLoggerDiagnosticsChannel()`。它会把订阅的 LoggerJS internals 发布到 Node `diagnostics_channel` channels:`loggerjs.dispatch`、`loggerjs.transport`、`loggerjs.flush`、`loggerjs.encode` 和 `loggerjs.worker`。
## Browser / Frontend(`@loggerjs/browser`)
| Transport | 功能 |
| --- | --- |
| `browserHttpTransport({ url })` | Batching HTTP delivery,带 offline queue、online replay with backoff,以及 page hide 上的 `sendBeacon`(按 `beaconMaxBytes` 切块)。 |
| `memoryBrowserHttpOfflineQueue()` | In-memory offline queue adapter(reload 后丢失)。 |
| `indexedDbBrowserHttpOfflineQueue()` | IndexedDB 中的 durable offline queue;跨 reload。 |
| `offlineFirstTransport(remote)` | 标准 remote + persistent queue wrapper;offline 或 remote delivery 失败时 queue,之后 replay。 |
| `indexedDbTransport()` | 把 logs 本地持久化到 IndexedDB,支持 session-aware indexes、TTL/count/byte pruning、durability hints、可选 Storage Bucket isolation、async `query()` API、`sessions()` 和 `stats()` observability。 |
| `browserWebSocketTransport({ socket })` | 通过 WebSocket 发送 codec-encoded batches;socket closed 时 queue(reconnection 由调用方负责)。 |
| `browserServiceWorkerTransport()` | 把 events post 给 service worker,在 active worker 可用前 queue;`target: "ready"` 时显式 `ready()` 等待 `serviceWorker.ready`。 |
| `browserBroadcastChannelTransport({ channel })` | 把 logs fan out 到其他 tabs(天然 lossy;receivers 必须正在监听)。 |
| `exportLogsToZip(source)` / `createLogZipBlob()` / `downloadBlob()` | 把 logs(例如来自 `indexedDbTransport().query()`)打包成带 manifest、可选 per-session files、可选 `recent.ndjson`/`recent.json` 和 CRC 的 ZIP,用于 support workflows。 |
`browserHttpTransport()` 同样接收 `transformPayload`。支持 `CompressionStream` 的浏览器使用 `browserCompressionPayloadTransform()`:
```ts
import { browserCompressionPayloadTransform, browserHttpTransport } from "@loggerjs/browser";
browserHttpTransport({
url: "/api/logs",
transformPayload: browserCompressionPayloadTransform({ format: "gzip" }),
});
```
现代 Chrome 上进行高吞吐本地浏览器采集时,优先使用独立 IndexedDB log store,并设置 relaxed durability:
```ts
indexedDbTransport({
durability: "relaxed",
localStorageSpill: {
maxBytes: 512 * 1024,
maxEntries: 200,
namespace: "loggerjs-support",
},
storageBucketName: "loggerjs-logs",
storageBucketDurability: "relaxed",
});
```
不支持 Storage Buckets 的浏览器会回退到普通 IndexedDB instance,同时保持相同 transport API。
`indexedDbTransport()` 默认分配 page-session id,把它作为 IndexedDB entry 顶层字段存储,并在事件缺少 `event.context.sessionId` 时把同一个值写入 context。传 `session: false` 可关闭这个物化 session 字段;传 `session: { id, getId, contextKey }` 可让持久化 session 对齐你自己的浏览器 context provider。
`localStorageSpill` 是 reload/close 前的最后机会保护,不是 IndexedDB 的替代品。正常日志仍然先进入内存 batch,并异步 flush 到 IndexedDB。`pagehide` 或 `visibilitychange: hidden` 时,transport 会同步把尚未确认落盘的尾部日志(`pendingFlushBatch` 加当前内存 buffer)写入一个很小的 `localStorage` temp entry。下一次 transport 实例会先把这个 temp entry drain 到 IndexedDB,写成功后才清除。它能降低普通 reload 和 tab close 时的丢失窗口,但不能防 process kill、browser crash、storage disabled、quota exhaustion 或 storage eviction。
### Browser failure boundaries
除非日志已经被你关心的 destination acknowledge,否则浏览器投递都是 best effort。关键丢失窗口:
| Path | Failure boundary / loss window | 生产建议 |
| --- | --- | --- |
| `browserHttpTransport()` | In-memory batches 在 reload、tab close、process kill,或 queue bound 在投递前 drop records 时丢失。Fetch 可能被 navigation abort。 | 需要 reload survival 时使用有界 queues、retry options 和 IndexedDB offline queue。 |
| `browserHttpTransport({ useBeaconOnPageHide: true })` | `sendBeacon` 是 fire-and-forget。浏览器会限制 payload size,并可能在 shutdown pressure 下 reject、truncate 或 skip delivery。 | 保守设置 `beaconMaxBytes`,把 pagehide flush 当最后机会,不要作为唯一 durability path。 |
| `memoryBrowserHttpOfflineQueue()` | 只要 page process 存活,可跨临时 offline periods。 | 轻量应用或测试可用;需要 support/debug logs 跨 reload 时改用 IndexedDB。 |
| `indexedDbBrowserHttpOfflineQueue()` | 跨 reload 存储 replay payloads,但 quota、private browsing mode、storage eviction、blocked upgrades 或 IndexedDB 不可用仍可能阻止持久化。 | 监控 queue/drop counters,保持 payloads 有界;搭配 HTTP replay 和 page lifecycle flush。 |
| `offlineFirstTransport(remote)` | remote delivery 失败时 queue,之后 replay。如果 local storage 失败或被驱逐,replay 不是保证。 | 优先使用 persistent queue adapter;可控 shutdown/navigation 时尽量调用 `flush()`。 |
| `indexedDbTransport()` | 本地持久性依赖 IndexedDB availability、quota、eviction policy、durability hints 和 Storage Buckets 支持。仍在内存 buffer 中、尚未完成 async IndexedDB write 的日志可能丢失。 | 可接受时用 `durability: "relaxed"` 提高吞吐;用 TTL/count/byte pruning 保持低于 quota。support logs 需要更可靠地跨普通 reload 时,启用有界 `localStorageSpill`。 |
| `browserWebSocketTransport()` | 页面退出、queue bound 超出或调用方从不 reconnect socket 时,queued events 可能丢失。 | 在 transport 外负责 reconnection,并用 queue bounds/drop counters 检测 backpressure。 |
| `browserServiceWorkerTransport()` | Delivery 依赖 service worker registration、activation、message delivery 和 worker lifetime。Terminating worker 会丢 in-flight work,除非它自己持久化 queue。 | 只把它当 centralization;除非 service worker 也写 durable storage,否则不要当 durability。 |
| `browserBroadcastChannelTransport()` | BroadcastChannel 只到达当前打开、同源、正在监听的 tabs。Messages 不 durable,receivers 启动期间会错过。 | 用于 multi-tab aggregation 和 debugging,不作为 primary remote delivery guarantee。 |
常见生产浏览器栈是 HTTP batching + IndexedDB offline queue + page lifecycle flush。需要跨 tab centralization 时再加 service worker 或 BroadcastChannel;日志必须跨 reload 时,delivery path 中仍要有 durable queue。
## Payload transforms
Payload transforms 在 codec encoding 后、wire transport 发送或存储 payload 前运行。它们可以返回替换 payload,或 `{ payload, headers, contentType }`;HTTP transports 会把这些 headers 持久化到 offline queues 并在 replay 时保留。
```ts
import {
composePayloadTransforms,
encryptionPayloadTransform,
} from "@loggerjs/core/payload-transforms";
import { browserCompressionPayloadTransform, browserHttpTransport } from "@loggerjs/browser";
browserHttpTransport({
url: "/api/logs",
transformPayload: composePayloadTransforms(
browserCompressionPayloadTransform(),
encryptionPayloadTransform({
contentType: "application/octet-stream",
headers: { "x-payload-encrypted": "1" },
encrypt: async (payload) => encryptForCollector(payload),
}),
),
});
```
`encryptionPayloadTransform()` 提供 hook;加密算法和 key management 仍由应用拥有。
## Vendor packages
Vendor HTTP transports 通过 `fetch` 直接实现 wire protocol。Sentry 和 OpenTelemetry bridge 这类 SDK/provider adapters 使用应用已初始化的 SDK object 或 provider。`otlpHttpTransport()` 会自包 `batchTransport`;Datadog、Elastic、Loki 和 CloudWatch 暴露 `logBatch`,需要 queueing、retry 或 circuit-breaker 时用 core reliability wrappers 包住。
生产 vendor 使用应把 reliability wrapper 写出来:
```ts
import { batchTransport } from "@loggerjs/core";
import { datadogLogsTransport } from "@loggerjs/datadog";
const transport = batchTransport(datadogLogsTransport({ apiKey: process.env.DD_API_KEY }), {
maxRecords: 100,
maxWaitMs: 2000,
maxQueueSize: 5000,
maxRetries: 3,
circuitBreakerFailureThreshold: 5,
});
```
| Package | Transport | Destination |
| --- | --- | --- |
| `@loggerjs/otel` | `otlpHttpTransport({ url })` | OTLP/HTTP JSON logs endpoint;导出 `otlpJsonCodec()` 和 mapping helpers。 |
| `@loggerjs/otel` | `openTelemetryLogBridgeTransport()` | Bridge into an OpenTelemetry `LoggerProvider`。 |
| `@loggerjs/sentry` | `sentryTransport({ sentry })` | Sentry structured logs、breadcrumbs、exception/message capture。 |
| `@loggerjs/datadog` | `datadogLogsTransport({ apiKey })` | Datadog Logs intake API。 |
| `@loggerjs/elastic` | `elasticTransport({ url, index })` | Elasticsearch `_bulk` API,支持 per-record index/pipeline/id selection。 |
| `@loggerjs/loki` | `lokiTransport({ url })` | Grafana Loki push API,带 stream labels 和 structured metadata。 |
| `@loggerjs/cloudwatch` | `cloudWatchLogsTransport({ ... })` | CloudWatch Logs `PutLogEvents`,内置 SigV4 signing。 |
| `@loggerjs/database` | `sqliteTransport()` / `postgresTransport()` / `databaseTransport(adapter)` | 通过 driver-agnostic adapters 做 batched inserts。 |
## 编写自定义 Transport
实现四类 delivery methods 中的任意一种。最简单的 event transport:
```ts
import type { Transport } from "@loggerjs/core";
const myTransport: Transport = {
name: "my-sink",
minLevel: "info",
log(event) {
push(JSON.stringify(event));
},
};
```
Record-aware transport 会进入 fast path(logger 没有 processors 时不投影 event):
```ts
import { fastEventJsonCodec } from "@loggerjs/codecs";
import { createPreparedRecordEncoder } from "@loggerjs/core";
const codec = fastEventJsonCodec();
const encodeRecord = createPreparedRecordEncoder(codec);
const recordSink: Transport = {
name: "record-sink",
write(record, context) {
push(encodeRecord(record));
// 需要 event shape?context.toEvent(record) 转换一次并 memoize,
// 其他 transports 会共享同一次 projection。
},
};
```
规则:
- 抛错(同步或 rejected promise)是安全的:错误会报告到 logger meta,其他 transports 继续运行。不要静默吞掉自己的错误,让它们暴露出来。
- 当调用方可以显式等待启动时,实现 `ready()`。`logger.ready()` 是 opt-in;普通日志调用永远不等待 transport readiness。
- 如果你会 buffer,实现 `flush()`;能在崩溃路径同步 drain 时实现 `flushSync()`;持有资源时实现 `close()`。
- 如果实现 `close()`,释放资源前包含自己的 best-effort flush。Core 有 `close()` 时会调用它;只有 transport 没有 `close()` 时才 fallback 到 `flush()`。
- 任何做 I/O 的 transport 优先使用 `logBatch`/`writeBatch` 加 `batchTransport`;per-event network calls 扛不住生产流量。
- 直接编码 raw records 会跳过 logger 的 `idFactory`;records 会得到文档化的 `defaultRecordId`。自定义 ids 很重要时,通过 `context.toEvent()` 转换。见 [编解码](CODECS.md)。
## 相关链接
- [英文原文](/TRANSPORTS)
- [中文首页](/zh/)
---
# 传输契约矩阵
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/TRANSPORT-CONTRACTS.md
---
title: "传输契约矩阵"
description: "每个内置 transport 对应的源码、公开入口、失败语义和测试证据。"
---
# 传输契约矩阵
这个矩阵把每个第一方 transport 固定到 public entry points、source files 和 contract tests。它的目的,是让可靠性声明可审计:raw wire transports 必须暴露投递失败;production-grade delivery 必须明确 wrappers 或 queues;lifecycle behavior 必须有命名 test path。
`pnpm verify:transport-contracts` 会把本页与 package exports 和仓库文件树核对。新增 transport source file 或 public transport subpath 时,必须在同一变更中更新本矩阵。
## 契约规则
- Raw HTTP/vendor transports 传播非 2xx responses 和 rejected `fetch` calls;除非被 wrapper 包住,否则不会静默 retry。
- Raw wire sinks 的生产投递需要 `batchTransport()`、`retryTransport()`、`fallbackTransport()`,或一个文档化了自身 queue/retry 行为的 transport。
- Runtime-dependent transports 必须记录它们依赖的平台表面,并测试 unavailable 或 failing dependencies。
- Durable paths 必须在测试中暴露 `flush()` 或 persistence/replay 行为。
- Test-only 和 display-only transports 必须明确说明,不要暗示 delivery durability。
## 矩阵
| ID | Public entry | Export(s) | Source | Delivery contract | Failure and lifecycle contract | Contract tests |
| --- | --- | --- | --- | --- | --- | --- |
| `core-batch` | `@loggerjs/core/transport-batch` | `batchTransport` | [packages/core/src/transports/batch.ts](packages/core/src/transports/batch.ts) | 围绕任意 inner transport 提供 bounded queue、byte budget、timed flush、concurrency、retry、circuit breaker 和 drop accounting。 | Failed batches 会按策略 retry 或重新入队;`flush()` drain;`close()` 停止 timers 并 flush pending work。 | [packages/core/test/batch-transport.test.ts](packages/core/test/batch-transport.test.ts)
[packages/core/test/batch-coverage.test.ts](packages/core/test/batch-coverage.test.ts) |
| `core-retry` | `@loggerjs/core/transport-reliability` | `retryTransport`, `fallbackTransport` | [packages/core/src/transports/reliability.ts](packages/core/src/transports/reliability.ts) | 为已有 batching 或需要 per-call retry 的 transports 提供 immediate retry/fallback wrapper。 | Retries 使用 bounded backoff;circuit breaker 和 fallback behavior 可观测;`flush()`/`close()` delegate 到 wrapped transports。 | [packages/core/test/reliability-transport.test.ts](packages/core/test/reliability-transport.test.ts)
[packages/core/test/reliability-coverage.test.ts](packages/core/test/reliability-coverage.test.ts) |
| `core-console` | `@loggerjs/core/transport-console` | `consoleTransport` | [packages/core/src/transports/console.ts](packages/core/src/transports/console.ts) | Runtime-neutral local console sink。 | 使用 unpatched console functions 避免 capture recursion;默认过滤 console-origin events。 | [packages/core/test/console-transport.test.ts](packages/core/test/console-transport.test.ts) |
| `core-memory` | Root export | `memoryTransport` | [packages/core/src/transports/memory.ts](packages/core/src/transports/memory.ts) | 有界 in-memory diagnostics ring buffer;不 durable。 | 超过容量时丢弃最旧 entries;snapshots 同步且 process-local。 | [packages/core/test/logger.test.ts](packages/core/test/logger.test.ts)
[packages/core/test/integration-api.test.ts](packages/core/test/integration-api.test.ts) |
| `core-test` | `@loggerjs/core/transport-test` | `testTransport` | [packages/core/src/transports/test.ts](packages/core/src/transports/test.ts) | Assertion-only in-memory sink,带 wait 和 snapshot helpers。 | Injectable failures 和 wait timeouts 属于测试契约;不是 production sink。 | [packages/core/test/test-transport.test.ts](packages/core/test/test-transport.test.ts) |
| `browser-http` | `@loggerjs/browser/transport-http` | `browserHttpTransport` | [packages/browser/src/http-transport.ts](packages/browser/src/http-transport.ts) | Browser fetch delivery,带 batching、retry options、可选 offline queue、payload transforms 和 page-exit beacon mode。 | 非 2xx 和 rejected fetch attempts 是 failures;page-exit beacon 是 best effort,受 size/runtime 影响。 | [packages/browser/test/http-transport.test.ts](packages/browser/test/http-transport.test.ts)
[tests/e2e/browser-production.spec.ts](tests/e2e/browser-production.spec.ts) |
| `browser-indexeddb-queue` | `@loggerjs/browser/offline-indexeddb`, `@loggerjs/browser/transport-indexeddb` | `indexedDbBrowserHttpOfflineQueue`, `indexedDbTransport` | [packages/browser/src/indexeddb-offline-queue.ts](packages/browser/src/indexeddb-offline-queue.ts)
[packages/browser/src/indexeddb-transport.ts](packages/browser/src/indexeddb-transport.ts) | Persistent browser queue/store,用于 reload-surviving replay 和 local export。 | IndexedDB unavailability、quota、private mode、blocked upgrades、TTL 和 pruning 都是 runtime-dependent 且有测试。 | [packages/browser/test/indexeddb-offline-queue.test.ts](packages/browser/test/indexeddb-offline-queue.test.ts)
[packages/browser/test/indexeddb-transport.test.ts](packages/browser/test/indexeddb-transport.test.ts)
[tests/e2e/browser-production.spec.ts](tests/e2e/browser-production.spec.ts) |
| `browser-offline-first` | `@loggerjs/browser/offline-first-transport` | `offlineFirstTransport` | [packages/browser/src/offline-first-transport.ts](packages/browser/src/offline-first-transport.ts) | Remote-first delivery,在 offline 或 remote failure 后 queued replay。 | Remote rejection 会 queue payloads;storage 和 network 可用时 `flush()` replay queued records。 | [packages/browser/test/offline-first-transport.test.ts](packages/browser/test/offline-first-transport.test.ts) |
| `browser-page-exit` | `@loggerjs/browser/transport-http`, `@loggerjs/browser/integration-page-lifecycle` | `browserHttpTransport` page lifecycle flush | [packages/browser/src/http-transport.ts](packages/browser/src/http-transport.ts)
[packages/browser/src/page-lifecycle.ts](packages/browser/src/page-lifecycle.ts) | Browser HTTP 的 last-chance pagehide/visibilitychange delivery。 | `sendBeacon` 是 best effort;unsupported 或 failed beacon 会在可能时 fallback 到 configured HTTP behavior。 | [packages/browser/test/http-transport.test.ts](packages/browser/test/http-transport.test.ts)
[packages/browser/test/page-lifecycle.test.ts](packages/browser/test/page-lifecycle.test.ts)
[tests/e2e/browser-production.spec.ts](tests/e2e/browser-production.spec.ts) |
| `browser-service-worker` | `@loggerjs/browser/transport-service-worker` | `browserServiceWorkerTransport` | [packages/browser/src/service-worker-transport.ts](packages/browser/src/service-worker-transport.ts) | Queue 到 configured 或 ready service worker controller 可用,再 post messages。 | Registration/controller availability 是 runtime-dependent;`ready()` 和 `flush()` 暴露 acceptance boundaries。 | [packages/browser/test/service-worker-transport.test.ts](packages/browser/test/service-worker-transport.test.ts)
[tests/e2e/browser-production.spec.ts](tests/e2e/browser-production.spec.ts) |
| `browser-websocket` | `@loggerjs/browser/transport-websocket` | `browserWebSocketTransport` | [packages/browser/src/websocket-transport.ts](packages/browser/src/websocket-transport.ts) | Live WebSocket sink,connecting 或 closed 时有 bounded queue。 | Reconnection 由调用方拥有;queue bounds 和 close/error behavior 明确。 | [packages/browser/test/websocket-transport.test.ts](packages/browser/test/websocket-transport.test.ts) |
| `browser-broadcast` | `@loggerjs/browser/transport-broadcast-channel` | `browserBroadcastChannelTransport` | [packages/browser/src/broadcast-channel-transport.ts](packages/browser/src/broadcast-channel-transport.ts) | 通过 BroadcastChannel 做 same-origin tab fan-out。 | Receiver presence 不保证;missing API 和 close semantics 有测试。 | [packages/browser/test/broadcast-channel-transport.test.ts](packages/browser/test/broadcast-channel-transport.test.ts) |
| `node-http` | `@loggerjs/node/transport-http` | `nodeHttpTransport` | [packages/node/src/http-transport.ts](packages/node/src/http-transport.ts) | Node fetch delivery,自包 `batchTransport`。 | 非 2xx/rejected fetch attempts 由 wrapper retry;`flush()` drain batched HTTP work。 | [packages/node/test/http-transport.test.ts](packages/node/test/http-transport.test.ts) |
| `node-file` | `@loggerjs/node/transport-file`, `@loggerjs/node/transport-rotating-file` | `fileTransport`, `rotatingFileTransport` | [packages/node/src/file-transport.ts](packages/node/src/file-transport.ts)
[packages/node/src/rotating-file-transport.ts](packages/node/src/rotating-file-transport.ts) | Local NDJSON file durability,可选 synchronous writes、buffering 和 size rotation。 | `flush()` drain 正常 writes;支持时 `flushSync()` 是 crash-path contract;rotation 是 single-writer。 | [packages/node/test/file-transport.test.ts](packages/node/test/file-transport.test.ts)
[packages/node/test/rotating-file-transport.test.ts](packages/node/test/rotating-file-transport.test.ts) |
| `node-stdout` | `@loggerjs/node/transport-stdout` | `stdoutTransport`, `stderrTransport` | [packages/node/src/stdout-transport.ts](packages/node/src/stdout-transport.ts) | stdout/stderr 的 NDJSON stream sink,带可选 buffering。 | Backpressure drain-aware;默认把 `EPIPE` 当 clean shutdown;`flush()` drain buffered writes。 | [packages/node/test/stdout-transport.test.ts](packages/node/test/stdout-transport.test.ts) |
| `node-syslog` | `@loggerjs/node/transport-syslog` | `nodeSyslogTransport` | [packages/node/src/syslog-transport.ts](packages/node/src/syslog-transport.ts) | UDP 或 TCP 上 RFC-style syslog formatting。 | UDP lossy;TCP 依赖 socket lifecycle;format、send、close 和 error behavior 有测试。 | [packages/node/test/syslog-transport.test.ts](packages/node/test/syslog-transport.test.ts) |
| `node-worker` | `@loggerjs/node/transport-worker` | `workerTransport` | [packages/node/src/worker-transport.ts](packages/node/src/worker-transport.ts) | Worker-thread offload,带 codec transfer 和可选 ready/ack handshake。 | Startup、ack timeout、fallback、close 和 auto-end behavior 定义 acceptance boundary。 | [packages/node/test/worker-transport.test.ts](packages/node/test/worker-transport.test.ts) |
| `database` | `@loggerjs/database/transport`, `@loggerjs/database/sqlite`, `@loggerjs/database/postgres` | `databaseTransport`, `sqliteTransport`, `postgresTransport` | [packages/database/src/transport.ts](packages/database/src/transport.ts)
[packages/database/src/sqlite.ts](packages/database/src/sqlite.ts)
[packages/database/src/postgres.ts](packages/database/src/postgres.ts) | Batched row mapping 到应用提供的 adapter、SQLite driver 或 Postgres client。 | Adapter/driver failures 通过 batch/retry 和 internal error reporting 暴露;transaction semantics 由 driver 拥有。 | [packages/database/test/database-transport.test.ts](packages/database/test/database-transport.test.ts) |
| `datadog` | `@loggerjs/datadog/transport` | `datadogLogsTransport` | [packages/datadog/src/index.ts](packages/datadog/src/index.ts) | Raw Datadog Logs HTTP payload sink。 | Missing fetch、非 2xx、rejected fetch 会让 raw transport 失败;生产 durability 需要 `batchTransport()` 或 `retryTransport()`。 | [packages/datadog/test/datadog-transport.test.ts](packages/datadog/test/datadog-transport.test.ts) |
| `elastic` | `@loggerjs/elastic/transport` | `elasticTransport` | [packages/elastic/src/index.ts](packages/elastic/src/index.ts) | Raw Elastic bulk HTTP payload sink。 | Missing fetch、非 2xx、rejected fetch 会让 raw transport 失败;生产 durability 需要 `batchTransport()` 或 `retryTransport()`。 | [packages/elastic/test/elastic-transport.test.ts](packages/elastic/test/elastic-transport.test.ts) |
| `loki` | `@loggerjs/loki/transport` | `lokiTransport` | [packages/loki/src/index.ts](packages/loki/src/index.ts) | Raw Loki push HTTP payload sink。 | Missing fetch、非 2xx、rejected fetch 会让 raw transport 失败;生产 durability 需要 `batchTransport()` 或 `retryTransport()`。 | [packages/loki/test/loki-transport.test.ts](packages/loki/test/loki-transport.test.ts) |
| `cloudwatch` | `@loggerjs/cloudwatch/transport` | `cloudWatchLogsTransport` | [packages/cloudwatch/src/index.ts](packages/cloudwatch/src/index.ts) | Raw CloudWatch Logs API payload sink。 | Missing fetch、非 2xx、rejected fetch 会让 raw transport 失败;sequence-token progression 仍依赖 caller/service。 | [packages/cloudwatch/test/cloudwatch-transport.test.ts](packages/cloudwatch/test/cloudwatch-transport.test.ts) |
| `sentry` | `@loggerjs/sentry/transport` | `sentryTransport` | [packages/sentry/src/index.ts](packages/sentry/src/index.ts) | Adapter 到应用提供的 Sentry SDK object。 | SDK throws 会让 raw transport 失败,并可被 wrappers 观察;capture-level mapping 和 disabled SDK methods 有测试。 | [packages/sentry/test/sentry-transport.test.ts](packages/sentry/test/sentry-transport.test.ts) |
| `otel-otlp` | `@loggerjs/otel/transport-http`, `@loggerjs/otel/codec-otlp-json` | `otlpHttpTransport`, `otlpJsonCodec` | [packages/otel/src/transport.ts](packages/otel/src/transport.ts)
[packages/otel/src/otlp-json.ts](packages/otel/src/otlp-json.ts) | OTLP/HTTP JSON delivery 自包 `batchTransport`;codec 把 LoggerJS events 映射为 OTLP logs。 | 非 2xx/rejected fetch attempts 由 wrapper retry;codec shape 和 metadata mapping 被固定。 | [packages/otel/test/otlp-json.test.ts](packages/otel/test/otlp-json.test.ts) |
| `pretty-console` | `@loggerjs/pretty/transport-console` | `prettyConsoleTransport` | [packages/pretty/src/console-transport.ts](packages/pretty/src/console-transport.ts) | 人类可读 browser/console display sink。 | Display-only;避免 console capture loops,并为 DevTools 保留 raw arguments。 | [packages/pretty/test/console-transport.test.ts](packages/pretty/test/console-transport.test.ts) |
| `pretty-stream` | `@loggerjs/pretty/transport-stream` | `prettyStreamTransport`, `prettyStdoutTransport`, `prettyStderrTransport` | [packages/pretty/src/stream-transport.ts](packages/pretty/src/stream-transport.ts) | Node terminals 或 stream-like targets 的人类可读 stream sink。 | Backpressure 和 stream errors 可见;支持时 `flush()` 等待 drain。 | [packages/pretty/test/stream-transport.test.ts](packages/pretty/test/stream-transport.test.ts) |
## 相关链接
- [英文原文](/TRANSPORT-CONTRACTS)
- [中文首页](/zh/)
---
# 友好输出
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/PRETTY.md
---
title: "友好输出"
description: "浏览器 DevTools 和 Node 终端的人类可读输出。"
---
# 友好输出
Pretty output 是本地 console 和 terminal 的开发体验层。LoggerJS 在内部仍然保持 records 结构化,只在 transport 边界把它们渲染成人类可读文本。
在开发、demo、本地调试、Storybook、浏览器 DevTools、CLI,以及需要可读输出的测试中使用 pretty transports。生产投递应使用结构化 transports,例如 `stdoutTransport()`、`fileTransport()`、`browserHttpTransport()`、OTLP、Loki 或 Datadog。
## Browser DevTools
当你希望浏览器 console 输出可读,同时对象仍可展开检查时,使用 `@loggerjs/pretty` 的 `prettyConsoleTransport()`:
```ts
import { createLogger } from "@loggerjs/core";
import { prettyConsoleTransport } from "@loggerjs/pretty/transport-console";
const logger = createLogger({
transports: [
prettyConsoleTransport({
browserStyles: "auto",
mode: "compact",
includeData: true,
includeContext: false,
}),
],
});
logger.info("cart updated", { itemCount: 3 });
```
它会:
- 当 `browserStyles: "auto"` 检测到 DevTools 时,在浏览器中使用 `%c` 样式。
- 把 `data`、`error` 和其他 details 作为独立 console 参数传入,让对象保持可展开。
- 通过 LoggerJS 的 unpatched console registry 写出,因此可以和 `captureConsoleIntegration()` 并存而不形成循环。
- 默认过滤来自 `captureConsoleIntegration()` 的 records。
## Node Terminal
本地终端输出使用 `prettyStdoutTransport()` 或 `prettyStderrTransport()`:
```ts
import { createLogger } from "@loggerjs/core";
import { prettyStdoutTransport } from "@loggerjs/pretty/transport-stream";
const logger = createLogger({
transports: [
prettyStdoutTransport({
colors: "auto",
mode: "expanded",
minLevel: "debug",
}),
],
});
```
它会:
- 目标 stream 是 TTY 时输出 ANSI 颜色。
- 尊重 `NO_COLOR` 和 `FORCE_COLOR`。
- 支持 `minLevel`。
- 当 stream 报告 backpressure 时,`flush()` 会等待 `drain`。
- `close()` 默认不会结束 `process.stdout` / `process.stderr`,除非设置 `endOnClose: true`。
## 共享 Formatter
自定义显示 transport 可以直接使用 formatter:
```ts
import { formatPrettyEvent } from "@loggerjs/pretty/formatter";
const rendered = formatPrettyEvent(event, {
colors: "never",
mode: "expanded",
includeTrace: true,
});
console.log(rendered.text);
```
`formatPrettyEvent()` 返回:
- `text`:纯文本输出。
- `ansiText`:`colors: "always"` 时用于终端的文本。
- `browserArgs`:带浏览器 `%c` 样式的 `console.*(...args)` 参数。
- `details`:给自定义 sink 使用的原始值和序列化文本。
## 选项建议
| 选项 | 用法 |
| --- | --- |
| `mode: "compact"` | 单行本地输出。适合浏览器 console 和繁忙 CLI。 |
| `mode: "expanded"` | 多行输出,每个 detail 一行。适合本地 Node 调试。 |
| `colors: "auto"` | 终端默认值:只有 stream 看起来可交互时使用 ANSI。 |
| `browserStyles: "auto"` | 浏览器默认值:只在浏览器类 runtime 中使用 CSS console styles。 |
| `includeData` / `includeError` | 默认开启,因为 pretty output 是给人读的。 |
| `includeContext` / `includeTrace` / `includeSource` / `includeId` | 默认关闭以保持可读;调试关联问题时再开启。 |
## 生产边界
Pretty transports 有意不承担持久化职责。它们不 batch、不 retry、不 persist,也不实现 collector wire protocol。生产 Node 服务优先使用:
```ts
import { stdoutTransport } from "@loggerjs/node";
stdoutTransport(); // NDJSON for collectors
```
本地开发中同时运行结构化输出和 pretty 输出是正常做法:
```ts
import { createLogger } from "@loggerjs/core";
import { stdoutTransport } from "@loggerjs/node";
import { prettyStderrTransport } from "@loggerjs/pretty";
const logger = createLogger({
transports: [
stdoutTransport({ minLevel: "info" }),
prettyStderrTransport({ minLevel: "debug", colors: "auto" }),
],
});
```
## 相关链接
- [英文原文](/PRETTY)
- [中文首页](/zh/)
---
# 集成
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/INTEGRATIONS.md
---
title: "集成"
description: "浏览器和 Node.js 自动采集,以及 integration API。"
---
# 集成
Integrations 会从平台行为中自动采集日志。它们始终是 **opt-in**:除非你配置了对应 integration,否则不会捕获任何内容。每次捕获都会带上 `source: "integration:"`,方便下游过滤和防循环。
启用哪些采集、如何清洗数据的隐私建议见 [运维](OPERATIONS.md)。
## Runtime 支持
| Runtime | 支持 | 数量 | 说明 |
| --- | --- | ---: | --- |
| Browser / frontend | `@loggerjs/browser` 中的一方 automatic collectors | 19 | Console、script errors、fetch/XHR、WebSocket、Web Vitals、Performance API、routing、user actions、service worker、extension/Electron renderer hooks 和 browser context propagation。 |
| Node.js / server | `@loggerjs/node` 中的一方 automatic collectors | 16 | Process crashes、diagnostics channels、HTTP frameworks、outgoing clients、CLI/serverless lifecycle、queues 和 database clients。 |
| Runtime-neutral / core | `@loggerjs/core` 只提供 Integration API | - | core package 定义 integration contract 和 loop-prevention helpers;平台采集位于 browser 和 Node.js packages。 |
自定义 integrations 应 feature-detect 平台表面,不可用时 no-op。
## 稳定性级别
Integration 稳定性描述 public setup/options contract 和 teardown behavior。它不表示底层平台在每个 runtime、浏览器版本、框架版本或部署模式中都会发出每个信号。
| 级别 | 含义 |
| --- | --- |
| Stable | 计划用于 v1-compatible application use。Option names、setup/teardown behavior 和高层 captured fields 受保护。 |
| Compatible | 公开且有测试,但 exact field shape 或 framework/runtime edge handling 在 v1 前仍可能细化。 |
| Runtime-dependent | Public API 稳定,但信号本身依赖 platform support、browser policy、framework hooks 或 deployment lifecycle behavior。 |
| Integration | Stability | 原因 |
| --- | --- | --- |
| `captureConsoleIntegration()` | Stable | 主要 browser capture primitive,带 loop prevention 和 teardown coverage。 |
| `captureBrowserErrorsIntegration()` | Stable | 标准 browser error 和 rejection capture;CSP 细节因浏览器而异。 |
| `captureFetchIntegration()` / `captureXHRIntegration()` | Stable | Request/response capture contract 稳定,并有显式 sanitization hooks。 |
| `pageLifecycleIntegration()` | Runtime-dependent | API 稳定,但 pagehide/visibility timing 由浏览器控制且 best effort。 |
| `captureWebVitalsIntegration()` | Runtime-dependent | 依赖 PerformanceObserver 和浏览器 metric support。 |
| `capturePerformanceIntegration()` | Runtime-dependent | Entry availability 因浏览器、permission policy 和 page lifecycle 不同。 |
| `captureReportingIntegration()` | Runtime-dependent | ReportingObserver 和 report types 在浏览器间不同。 |
| `captureRouterIntegration()` | Stable | generic browser routing 的 history/hash capture 稳定。 |
| Framework router adapters | Compatible | Public adapters 有测试,但 framework-specific hook shapes 可能演进。 |
| `captureFrameworkErrorsIntegration()` | Compatible | Public helper API 稳定;framework error hook payloads 仍由框架拥有。 |
| `captureUserActionsIntegration()` | Compatible | Privacy-first defaults 稳定;element metadata heuristics 可能调整。 |
| `captureWebSocketIntegration()` | Compatible | Constructor patching 和 event capture 公开;sampled message details 可能演进。 |
| `captureServiceWorkerIntegration()` | Runtime-dependent | 依赖 service worker availability 和 lifecycle messages。 |
| `captureRuntimeHostIntegration()` | Runtime-dependent | Extension 和 Electron surfaces 是 host-specific,并按 channel 显式 opt-in。 |
| `browserContextPropagationIntegration()` | Stable | Ambient context binding contract 稳定。 |
| `captureProcessIntegration()` | Stable | Node crash/warning/exit capture 和有界 flush 行为是生产承诺。 |
| `diagnosticsChannelIntegration()` | Runtime-dependent | Node channel names 和 payloads 来自 Node 及已 instrumented libraries。 |
| HTTP framework integrations | Compatible | Express/Fastify/Koa/Nest/Hapi adapters 公开;framework lifecycle details 可能调整。 |
| `nodeFetchIntegration()` / `nodeHttpClientIntegration()` | Compatible | Outgoing HTTP capture 公开;Node/undici/http edge details 可能演进。 |
| `captureCliIntegration()` / `serverlessIntegration()` | Compatible | Lifecycle contract 公开;platform-specific invocation metadata 可能细化。 |
| `queueIntegration()` / `bullMqIntegration()` | Compatible | Generic 和 BullMQ operation capture 公开;queue payload metadata 有意可配置。 |
| `databaseIntegration()` / `prismaIntegration()` / `redisIntegration()` | Compatible | Data-client method wrapping 公开;statement/command extraction heuristics 可能演进。 |
## Import Boundaries
Root package imports 是方便入口。Public integration subpaths 被明确记录,用户可以选择更窄 bundles,新内置 integrations 也不能在没有文档时悄悄扩大 surface。
| Runtime | Public integration subpaths |
| --- | --- |
| Browser | `@loggerjs/browser/integration-console`, `@loggerjs/browser/integration-context`, `@loggerjs/browser/integration-errors`, `@loggerjs/browser/integration-fetch`, `@loggerjs/browser/integration-xhr`, `@loggerjs/browser/integration-framework-errors`, `@loggerjs/browser/integration-framework-routers`, `@loggerjs/browser/integration-reporting`, `@loggerjs/browser/integration-router`, `@loggerjs/browser/integration-runtime-host`, `@loggerjs/browser/integration-service-worker`, `@loggerjs/browser/integration-user-actions`, `@loggerjs/browser/integration-websocket`, `@loggerjs/browser/integration-web-vitals`, `@loggerjs/browser/integration-performance`, `@loggerjs/browser/integration-page-lifecycle` |
| Node.js | `@loggerjs/node/integration-process`, `@loggerjs/node/integration-cli`, `@loggerjs/node/integration-koa`, `@loggerjs/node/integration-nest`, `@loggerjs/node/integration-hapi`, `@loggerjs/node/integration-prisma`, `@loggerjs/node/integration-redis`, `@loggerjs/node/integration-queue`, `@loggerjs/node/integration-bullmq`, `@loggerjs/node/integration-serverless`, `@loggerjs/node/integration-database`, `@loggerjs/node/integration-express`, `@loggerjs/node/integration-fastify`, `@loggerjs/node/integration-fetch`, `@loggerjs/node/integration-http-client`, `@loggerjs/node/integration-diagnostics` |
当 public integration subpath 被导出但未列在这里时,`pnpm verify:component-docs` 会失败。新增 entries 也应更新上方稳定性表和该 integration family 的 runtime validation notes。
## Browser / Frontend(`@loggerjs/browser`)
| Integration | 捕获内容 | 说明 |
| --- | --- | --- |
| `captureConsoleIntegration()` | `console.debug/info/log/warn/error/trace` calls | Level allowlist(`levels`)、rate limit(默认 100/s)。Teardown 时恢复 patched methods;console transport 通过 unpatched methods 写出,所以不会循环。 |
| `captureBrowserErrorsIntegration()` | `window.onerror` script/resource errors、`unhandledrejection`、可选 CSP violations | 对快速重复的相同 script errors 做 dedupe。 |
| `captureFetchIntegration()` | 失败(status >= `minStatus`,默认 400)和采样成功的 `fetch` calls | Header allowlists、URL sanitizer。捕获后仍把 errors re-throw 给应用。 |
| `captureXHRIntegration()` | `XMLHttpRequest` lifecycle,带 status 和 duration | 与 fetch 相同的 sanitization options。 |
| `pageLifecycleIntegration()` | 在 `pagehide` / `visibilitychange` 上 flush transports | 合并快速重复 flush;与 HTTP transport 的 beacon mode 搭配。 |
| `captureWebVitalsIntegration()` | CLS、FCP、INP、LCP、TTFB | 通过 PerformanceObserver 发出 incremental 和 final values。 |
| `capturePerformanceIntegration()` | navigation、resource、longtask、measure、mark entries | Deduplicated,并受 `maxEntries` 限制。 |
| `captureUserActionsIntegration()` | clicks、inputs、submits | Per-element throttling;默认不捕获 text/value。 |
| `captureRouterIntegration()` | route changes(`pushState`/`replaceState`/`popstate`/`hashchange`) | 可选 state normalization。 |
| `captureReportingIntegration()` | ReportingObserver reports(CSP、deprecation、intervention、crash) | Teardown 时 drain pending reports。 |
| `captureServiceWorkerIntegration()` | service worker lifecycle、messages、message errors | 默认不捕获 message data。 |
| `captureWebSocketIntegration()` | WebSocket connect/open/close/error 和 sampled messages | 包装 constructor;setup 前创建的 sockets 不会被跟踪。 |
| `captureFrameworkErrorsIntegration()` | React/Vue/Solid/Svelte error hooks | 暴露 `reactComponentDidCatch()`、`vueErrorHandler()` 等;buffer logger 存在前抛出的 errors(`maxPending`)。 |
| `captureRuntimeHostIntegration()` | browser-extension messages、configured channels 上的 Electron IPC | 保守默认:不监控任何 channels。 |
| `browserContextPropagationIntegration()` | session/request/action 和 trace context | 为 traceparent、baggage、session id、request id、recent user action 添加 ambient context providers。 |
| `nextRouterIntegration()` / `reactRouterIntegration()` / `vueRouterIntegration()` / `nuxtRouterIntegration()` | framework router transitions | 常见 router APIs 的 thin adapters;记录前 sanitize URLs。 |
## Node.js / Server(`@loggerjs/node`)
| Integration | 捕获内容 | 说明 |
| --- | --- | --- |
| `captureProcessIntegration()` | `uncaughtException`(fatal)、`unhandledRejection`、warnings、exit | 配置 `exitOnUncaught` 后,捕获 fatal record,调用 `flushSync()`,最多等待 `flushTimeoutMs` 的 async `flush()`,然后以 code `1` 退出。 |
| `diagnosticsChannelIntegration()` | Node `diagnostics_channel` messages(http、undici、custom channels) | 默认不捕获 message payload。 |
| `expressIntegration(logger)` | request completion,带 status、route、duration、request id | 返回 Express middleware;可选 per-request `withContext` binding。 |
| `fastifyIntegration(logger)` | 通过 onRequest/onError/onResponse hooks 捕获 request lifecycle | 返回 Fastify plugin;state 存在 WeakMap 中。 |
| `nodeFetchIntegration()` | outgoing `fetch` calls,带 status 和 duration | 捕获后 errors 会 re-throw。 |
| `nodeHttpClientIntegration()` | `http.request` / `http.get` calls | 捕获 Node http client 生命周期。 |
| `captureCliIntegration()` | CLI start、exit code、SIGINT/SIGTERM | 按 token/password/secret patterns 清洗 argv。 |
| `serverlessIntegration(logger, handler)` | 包装 serverless handler:invocation、duration、cold start、errors | 支持 promise、callback 和 sync handlers。 |
| `queueIntegration()` | queue client operations(publish/consume/ack/nack),带 duration | 按 client 列表 patch 指定 methods。 |
| `databaseIntegration()` | database client calls(query/execute/...),带 statement 和 duration | Statement 从第一个 string arg 或 `.sql`/`.text`/`.query` 属性提取。 |
| `koaIntegration()` / `nestMiddlewareIntegration()` / `hapiIntegration()` | framework request lifecycle | Koa、Express-compatible Nest middleware、Hapi request hooks 的 thin adapters。Nest adapter 不 hook Nest exception filters、interceptors、guards 或原始 thrown `Error`。 |
| `prismaIntegration()` | Prisma raw-query methods | 只包装 `$queryRaw` / `$executeRaw` raw-query variants。不订阅 `$on("query")`,也不捕获 `prisma.user.findMany()` 这类 typed model operations。 |
| `redisIntegration()` | Redis command methods | 捕获 selected command methods、duration、errors 和可选 payload metadata。 |
| `bullMqIntegration()` | BullMQ Queue method calls | 包装 `add`、`addBulk` 和存在时的 legacy `process` method。不 hook `Worker` 或 `QueueEvents` 的 `completed`、`failed`、`stalled` 等 lifecycle events。 |
### Context manager
这不是 integration,但也是启动时安装一次:
```ts
import { installAsyncLocalStorageContext } from "@loggerjs/node";
installAsyncLocalStorageContext();
```
之后 `withContext()` 的值会跨 `await` 边界跟随 async execution。
## 编写自定义 Integration
```ts
import type { Integration } from "@loggerjs/core";
export function captureThingIntegration(): Integration {
return {
name: "thing",
setup(api) {
const original = thing.onEvent;
const capture = api.guard((payload: unknown) => {
api.capture({
level: "info",
message: "thing event",
data: { payload },
});
});
thing.onEvent = (payload) => {
capture(payload);
return original(payload);
};
return () => {
thing.onEvent = original;
};
},
};
}
```
setup context(`api`)提供:
- `capture(input)`:主要入口;打上 `source: "integration:thing"`。
- `log/trace/debug/info/warn/error/fatal/event/captureException`:当 capture semantics 不合适时使用的直接 logging methods。
- `guard(fn)`:用 re-entrancy counter 包装 callback。如果 patched surface 本身被 logging pipeline 触发(典型情况:console capture + console transport),递归调用会被丢弃并计入 meta(`integration.dropped.reentrant`),而不是无限递归。
- `unpatched`:原始 `console.*` / `fetch` / `XMLHttpRequest` 实现的注册表,在所有 integrations 间共享,让 double patching 可以组合。
- `flush/flushSync/close`:供 page hide 等 lifecycle-driven integrations 使用。
规则:
- 始终返回 teardown,恢复你 patch 的内容。Teardowns 在 `logger.close()` 时运行一次,并按 setup 反向顺序执行。
- Setup 对每个 integration _instance_ 是幂等的;创建两个 instances 就会 patch 两次。导出 factory 并写清楚。
- 优雅降级:feature-detect 平台表面,不存在时 no-op。
- 捕获原始结构化数据,让 processors 负责 redact;不要提前格式化 messages。
## 相关链接
- [英文原文](/INTEGRATIONS)
- [中文首页](/zh/)
---
# 处理器
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/PROCESSORS.md
---
title: "处理器"
description: "middleware / processor 目录、顺序和使用场景。"
---
# 处理器和 Middleware
`@loggerjs/processors` 是管线中间层的 runtime-neutral 工具箱。这里的所有能力都是同步、有序、错误隔离的:抛错的 processor 会被报告到 logger meta,管线继续运行。
存在两种形态(完整模型见 [核心概念](CONCEPTS.md)):
- **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) => boolean` matcher。
- `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。 |
## 顺序建议
顺序很重要,每个阶段看到的是前一阶段输出:
1. **先 enrich**(tags、context、trace),让后续阶段能匹配这些字段。
2. **再 normalize**(errors、stacks),然后做依赖错误形状的 fingerprint 或 match。
3. **在检查 data 的采样决策之前先 redact**,并且始终在任何内容离开进程前 redact。
4. **最后做流量控制**(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 一定运行成功。
## 相关链接
- [英文原文](/PROCESSORS)
- [中文首页](/zh/)
---
# 编解码
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/CODECS.md
---
title: "编解码"
description: "序列化归属、快速默认行为和自定义 codec 合约。"
---
# 编解码
Codec 把日志 records 或 events 转成字节,也可以可选地反向解析。Codecs 属于 **transports**:管线内部保持原始值,让 redaction 和 routing 能处理结构化数据,并让每个目的地选择自己的 wire format。
## 合约
```ts
interface Codec {
name: string;
contentType: string;
encode(input: LogEvent | LogRecord | readonly (LogEvent | LogRecord)[], context?: EncodeContext): TPayload;
decode?(payload: TPayload): LogEvent | LogEvent[];
prepareRecordEncoder?(hints: RecordEncoderHints): PreparedRecordEncoder;
}
```
- `encode` 接收单项或批量,接收 events 或 records。core 的 `normalizeCodecInput()` 会把 records 投影成 events,供只理解 events 的 codecs 使用。
- `decode` 是可选的;内置 codecs 用 `JSON.parse` 实现对自身输出的对称 round trip。
- `prepareRecordEncoder` 是可选的。Record-aware transports 可以调用 `createPreparedRecordEncoder(codec)`,让 codec 缓存稳定的 category/tags 片段,同时仍然由 transport 拥有序列化。
## 内置 Codecs
| Codec | Package | 行为 |
| --- | --- | --- |
| `jsonCodec()` | core | 输入标准化后直接 `JSON.stringify`。速度快,但 circular/BigInt 会抛错;只在 payload 保证干净时使用。 |
| `safeJsonCodec(options)` | core | 每次完整安全标准化:circular -> `"[Circular]"`、BigInt -> string、Error -> `{name, message, stack}`、深度/数组/key 截断、Map/Set 转换。`consoleTransport({ pretty: false })` 的默认 codec。 |
| `ndjsonCodec(options)` | core | 每行一个 JSON object。遵守下方的 **fast-by-default 合约**。Node stdout/file transports 的默认 codec。 |
| `fastEventJsonCodec(options)` | `@loggerjs/codecs` | 性能 codec:native fast path、片段缓存(level、logger、tags、time)、扫描式字符串转义、flat-data 直接写入、lean envelope 选项。 |
| `pinoCompatCodec(options)` / `pinoNdjsonProjector(options)` | `@loggerjs/codecs` | 迁移用 Pino 形状 NDJSON:`level`、`time`、可选 `pid`/`hostname` base fields、`msg`、`err`,以及带保留键保护的可选 root data merge。 |
| `msgpackrCodec(options?)` | `@loggerjs/codecs` | 基于 `msgpackr` 的内置 MessagePack codec;返回 `Uint8Array`。仍支持传入 `{ pack, unpack }` 适配自定义 runtime。 |
| `projectorCodec(options)` | `@loggerjs/codecs` | 通用 project -> serialize(-> parse -> unproject)适配器,用于自定义 wire schema。 |
| `otlpJsonCodec(options)` | `@loggerjs/otel` | 带 resource attributes 的 OTLP/HTTP JSON log payload。 |
## Fast-by-Default 合约
`ndjsonCodec()` 和 `fastEventJsonCodec()` 共享同一套文档化行为:
- **不设置任何选项**:encode 走 native fast path。输出遵循原生 `JSON.stringify` 语义:data 中嵌套的原始 `Error` 会序列化为 `{}`,没有深度截断。会让 native stringify *抛错* 的输入(circular references、BigInt)会透明地用 safe stringifier 重新编码;日志行不会因为编码失败而丢失,每次 fallback 都会增加 `codec.fallback` meta counter。
- **设置任何 `SafeStringifyOptions` 字段**(`maxDepth`、`maxArrayLength`、`maxObjectKeys`、`includeStack`、`stable`、`space`):codec 会对每一项启用完整安全标准化,也会保留 data payload 中 `Error` 的 name/message/stack。
这需要显式选择:native-fast 加 throw-protection,或完整标准化。`safeJsonCodec` 始终是 always-safe。
## Lean Envelope 选项
`fastEventJsonCodec` 可以裁剪 envelope,以获得最小 NDJSON 输出:
```ts
fastEventJsonCodec({
includeId: false, // 也会在 record path 上完全跳过 id 计算
includeSeq: false,
includeLevelName: false,
// includeData / includeError / includeContext / includeTrace / includeSource
})
```
关闭三个 header flags 后,输出是 lean LoggerJS envelope:`{"time":...,"level":30,"logger":"api","message":"...","data":{...}}`。这是 [基准](BENCHMARKS.md) 中头条数字使用的配置;record-aware custom transports 可以把它和 `createPreparedRecordEncoder(codec)` 搭配,走最快的稳定片段路径。需要 Pino 字段名(`msg`、`err`、`pid`、`hostname`)时使用 `pinoCompatCodec()`。
## Pino 兼容
`pinoCompatCodec()` 输出 newline-delimited JSON,适合需要 Pino 形状输出的迁移路径:
```ts
import { pinoCompatCodec } from "@loggerjs/codecs";
pinoCompatCodec({
base: { pid: process.pid, hostname: "api-1" },
mergeData: true,
});
```
Root data merging 是 opt-in。默认情况下,LoggerJS 把 payload 保留在 `data` 下;启用 `mergeData: true` 后,`time`、`level`、`msg`、`pid`、`hostname`、`err`、`logger`、`data` 等保留键会被嵌套,避免覆盖 transport-owned fields。迁移测试时如果希望拒绝这类 payload,可以设置 `collision: "throw"`。
这个 codec 有意只支持 encode:Pino-shaped NDJSON 是迁移 wire format,不是 LoggerJS 原生 event envelope。
## Records、Events 和 IDs
编码原始 `LogRecord`(fast path)与编码 events 有一个语义差异:records 没有 id,所以 codec 会写入 `defaultRecordId(record, levelName)`,即 `time36-seq36-levelName` 字符串,与 `recordToEvent()` 分配的 id 一致。影响是:
- 使用默认 id factory 时,record-encoded 和 event-encoded 输出完全相同。
- logger 上自定义的 `idFactory` 会被 record-direct encoding **绕过**。如果自定义 id 很重要,transport 应通过 `context.toEvent(record)` 转换(memoized,会应用 id factory),而不是直接编码 record。
## 编写自定义 Codec
```ts
import { normalizeCodecInput, type Codec } from "@loggerjs/core";
export function csvCodec(): Codec {
return {
name: "csv",
contentType: "text/csv",
encode(input) {
const events = normalizeCodecInput(input);
const list = Array.isArray(events) ? events : [events];
return list
.map((e) => `${e.time},${e.levelName},${JSON.stringify(e.logger)},${JSON.stringify(e.message)}`)
.join("\n");
},
};
}
```
建议:
- **不要让 `encode` 抛出。** 包住风险路径,并 fallback 到 core 的 `safeJsonStringify`;用 `incrementLoggerMetaCounter("codec.fallback")` 计数。抛错的 codec 会变成 transport failure;在 batch transport 内还会形成消耗 retries 的 poison batch。
- 除非你有意实现 record fast path,否则使用 `normalizeCodecInput()`。
- 如果实现 `prepareRecordEncoder`,同一 record 的输出必须和 `encode(record)` 字节级一致,并保持相同 fallback 行为。
- 二进制格式返回 `Uint8Array`,并设置准确的 `contentType`;HTTP transports 会发送它。
- 只有当你的功能需要对称 round trip(replay、本地查询)时才实现 `decode`;投递不要求它。
## 相关链接
- [英文原文](/CODECS)
- [中文首页](/zh/)
---
# 生产配方
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/PRODUCTION-RECIPES.md
---
title: "生产配方"
description: "浏览器 HTTP/offline、Node stdout+OTLP、Loki、Datadog 等部署组合。"
---
# 生产配方
这些配方是生产部署起点。它们有意展示 queue bounds、privacy processors、shutdown behavior,以及 credentials 应放在哪里。请按应用调整 names、tags 和 endpoint URLs。
## 浏览器到 HTTP,并用 IndexedDB 离线重放
当浏览器日志需要跨网络中断和普通 reload 存活时使用。浏览器仍然无法保证在 process kill、storage eviction、private browsing restrictions 或 quota exhaustion 下投递成功。
```ts
import {
browserHttpTransport,
captureBrowserErrorsIntegration,
captureConsoleIntegration,
captureFetchIntegration,
captureWebVitalsIntegration,
createLogger,
indexedDbBrowserHttpOfflineQueue,
pageLifecycleIntegration,
} from "@loggerjs/browser";
import { privacyGuardProcessor, redactProcessor } from "@loggerjs/processors";
const offlineQueue = indexedDbBrowserHttpOfflineQueue({
dbName: "checkout-web-http-offline",
storeName: "http-offline",
maxEntries: 5000,
dropPolicy: "drop-oldest",
});
export const logger = createLogger({
category: ["web"],
level: "info",
tags: {
service: "checkout-web",
env: "production",
runtime: "browser",
},
processors: [
redactProcessor({
keys: ["password", "token", "authorization", "cookie", /secret/i],
}),
privacyGuardProcessor({
maxStringLength: 8192,
}),
],
transports: [
browserHttpTransport({
name: "browser-http",
url: "/api/logs",
maxBatchSize: 50,
flushIntervalMs: 2000,
maxQueueSize: 2000,
dropPolicy: "drop-oldest",
offlineQueue,
offlineReplayMaxRetries: 3,
offlineReplayBaseDelayMs: 250,
offlineReplayMaxDelayMs: 5000,
useBeaconOnPageHide: true,
beaconMaxBytes: 60 * 1024,
}),
],
integrations: [
captureConsoleIntegration({
levels: ["warn", "error"],
captureArguments: false,
maxCapturesPerSecond: 50,
}),
captureBrowserErrorsIntegration({
captureSecurityPolicyViolation: true,
}),
captureFetchIntegration({
minStatus: 400,
captureRequestHeaders: ["content-type", "x-request-id"],
captureResponseHeaders: ["content-type", "x-request-id"],
sanitizeUrl: (url) => {
const parsed = new URL(url, location.origin);
parsed.search = "";
return parsed.toString();
},
}),
captureWebVitalsIntegration({ flushOnHidden: true }),
pageLifecycleIntegration(),
],
});
```
生产说明:
- `/api/logs` 应该是你自己的 collector endpoint。不要把 vendor API keys 放进浏览器 bundle。
- fetch/XHR header capture 保持 allowlist。默认不要捕获 cookies、authorization headers、request bodies 或 form values。
- 当应用能暴露 logger meta counters 和 offline queue depth 时,对 `transport.dropped.*` 等指标告警。
- HTTP offline queue 的 `dbName` 应与可查询的 support-log store 分开。两个 helper 使用独立的 IndexedDB schema 和版本生命周期。
## 浏览器 Support Export,并使用 Session-Aware IndexedDB
当 support 或 QA 需要一份可本地保留、可按页面 session 导出的日志包时使用。这个本地 store 和 HTTP delivery queue 分开:IndexedDB 是可查询的事实来源,`localStorageSpill` 只保护用户刷新或关闭页面时尚未完成 async IndexedDB write 的小尾巴。
```ts
import {
createLogger,
downloadBlob,
exportLogsToZip,
indexedDbTransport,
} from "@loggerjs/browser";
import { privacyGuardProcessor, redactProcessor } from "@loggerjs/processors";
const supportStore = indexedDbTransport({
name: "support-indexeddb",
dbName: "checkout-web-support-logs",
storeName: "support-logs",
maxEntries: 20_000,
maxBytes: 25 * 1024 * 1024,
ttlMs: 7 * 24 * 60 * 60 * 1000,
batchSize: 50,
flushIntervalMs: 1000,
durability: "relaxed",
localStorageSpill: {
namespace: "checkout-support-logs",
maxEntries: 200,
maxBytes: 512 * 1024,
minLevel: "info",
},
});
export const supportLogger = createLogger({
category: ["web"],
level: "info",
processors: [
redactProcessor({
keys: ["password", "token", "authorization", "cookie", /secret/i],
}),
privacyGuardProcessor({ maxStringLength: 8192 }),
],
transports: [supportStore],
});
export async function downloadSupportLogZip() {
await supportLogger.flush();
const zip = await exportLogsToZip(supportStore, {
groupBySession: true,
includeRecent: { maxEvents: 500 },
query: {
from: Date.now() - 7 * 24 * 60 * 60 * 1000,
order: "asc",
},
source: "indexeddb",
});
downloadBlob(zip, `checkout-logs-${new Date().toISOString().replace(/[:.]/g, "-")}.zip`);
}
```
生产说明:
- 隐私 processor 必须在 IndexedDB transport 前执行。任何本地持久化内容都可能被用户或 support flow 导出。
- `indexedDbTransport()` 默认创建 page-session id,并在缺失时写入 IndexedDB entry metadata 和 `event.context.sessionId`。如果应用已有 session id,传 `session: { id, getId, contextKey }`。
- `localStorageSpill` 是有界、best-effort 的最后机会保护。它改善普通 reload 和 close 行为,但不能防 process kill、crash、storage disabled、quota exhaustion 或 storage eviction。
## Node 到 Stdout 加 OTLP
把 stdout 作为本地、平台原生 sink,把 OTLP 作为远程 observability path。即使 OTLP endpoint 降级,stdout 对 container runtimes 和 fatal events 仍有价值。
```ts
import * as otelApi from "@opentelemetry/api";
import {
captureProcessIntegration,
createLogger,
installAsyncLocalStorageContext,
stdoutTransport,
} from "@loggerjs/node";
import { openTelemetryTraceProcessor, otlpHttpTransport } from "@loggerjs/otel";
import { redactProcessor } from "@loggerjs/processors";
installAsyncLocalStorageContext();
export const logger = createLogger({
category: ["api"],
level: "info",
tags: {
service: "checkout-api",
env: process.env.NODE_ENV ?? "production",
runtime: "node",
},
processors: [
openTelemetryTraceProcessor({ api: otelApi }),
redactProcessor({
keys: ["password", "token", "authorization", "cookie", /secret/i],
}),
],
transports: [
stdoutTransport({
name: "stdout",
minLength: 4096,
}),
otlpHttpTransport({
name: "otlp",
url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ?? "http://localhost:4318/v1/logs",
headers: process.env.OTEL_EXPORTER_OTLP_AUTHORIZATION
? { authorization: process.env.OTEL_EXPORTER_OTLP_AUTHORIZATION }
: undefined,
resource: {
"service.name": "checkout-api",
"deployment.environment": process.env.NODE_ENV ?? "production",
},
maxRecords: 100,
maxWaitMs: 2000,
maxQueueSize: 5000,
maxRetries: 3,
circuitBreakerFailureThreshold: 5,
circuitBreakerResetMs: 30000,
}),
],
integrations: [
captureProcessIntegration({
exitOnUncaught: true,
flushTimeoutMs: 500,
}),
],
});
export async function closeLogger() {
await logger.close();
}
```
生产说明:
- fatal process paths 至少保留一个本地 sink(`stdoutTransport()` 或 `fileTransport()`)。Remote OTLP 不应是唯一 crash-path sink。
- 需要 active span correlation 时,在构造 logger 前安装 `@opentelemetry/api` 并初始化 tracing。
- 使用部署平台的 graceful shutdown hook 调用 `logger.close()`。
## 全栈投递到 Loki 和 Datadog
当浏览器和服务端日志需要进入同一 vendor backends 时使用。浏览器把日志发送到你自己的 collector;服务器持有 Loki 和 Datadog credentials,并转发 server-side events 和已接受的 browser batches。
```ts
import {
batchTransport,
createLogger,
recordToEvent,
type LogEvent,
type Transport,
type TransportContext,
} from "@loggerjs/core";
import { datadogLogsTransport } from "@loggerjs/datadog";
import { lokiTransport } from "@loggerjs/loki";
import { redactProcessor } from "@loggerjs/processors";
const service = "checkout";
const env = process.env.NODE_ENV ?? "production";
function reliableVendorTransport(transport: Transport): Transport {
return batchTransport(transport, {
maxRecords: 100,
maxWaitMs: 2000,
maxQueueSize: 10000,
dropPolicy: "drop-oldest",
maxRetries: 3,
retryBaseDelayMs: 250,
retryMaxDelayMs: 5000,
circuitBreakerFailureThreshold: 5,
circuitBreakerResetMs: 30000,
});
}
const vendorTransports = [
reliableVendorTransport(
lokiTransport({
url: process.env.LOKI_URL ?? "http://localhost:3100/loki/api/v1/push",
tenantId: process.env.LOKI_TENANT_ID,
labels: { service, env },
labelTags: ["runtime"],
structuredMetadata: true,
}),
),
reliableVendorTransport(
datadogLogsTransport({
apiKey: process.env.DD_API_KEY,
site: process.env.DD_SITE ?? "datadoghq.com",
service,
source: "loggerjs",
tags: { env },
eventTagKeys: ["runtime"],
}),
),
];
export const serverLogger = createLogger({
category: ["api"],
level: "info",
tags: { service, env, runtime: "node" },
processors: [
redactProcessor({
keys: ["password", "token", "authorization", "cookie", /secret/i],
}),
],
transports: vendorTransports,
});
const collectorContext: TransportContext = {
loggerName: "browser-log-collector",
now: () => Date.now(),
toEvent: recordToEvent,
reportInternalError(error, detail) {
serverLogger.warn("browser log collector failed", { error, detail });
},
};
export async function forwardBrowserLogs(events: LogEvent[]) {
for (const transport of vendorTransports) {
if (transport.logBatch) await transport.logBatch(events, collectorContext);
else {
for (const event of events) await transport.log?.(event, collectorContext);
}
}
}
```
生产说明:
- 调用 `forwardBrowserLogs()` 前验证并限制 `/api/logs` request body。过大的 batches 应尽早拒绝。
- 只有低基数字段应该提升为 Loki labels 和 Datadog tags。User ids、request ids、order ids 和 URLs 放在 structured metadata/data 中。
- 浏览器和服务端 collector 使用同一套 redaction policy。把 browser-submitted logs 视为不可信输入。
## 相关链接
- [英文原文](/PRODUCTION-RECIPES)
- [中文首页](/zh/)
---
# 运维
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/OPERATIONS.md
---
title: "运维"
description: "隐私、离线重放、崩溃路径、可靠性和 trace 关联。"
---
# 运维
本指南覆盖最影响 LoggerJS 生产行为的部分:隐私、浏览器缓冲、崩溃路径和远程投递可靠性。
## 隐私
自动 integrations 都是 opt-in。只启用产品实际需要的捕获表面。
推荐默认值:
- 在任何 remote transport 之前使用 `redactProcessor()`。
- 在 fetch/XHR integrations 中使用 HTTP header allowlist。默认不要发送 cookies、authorization headers 或完整 request bodies。
- 当 query string 可能包含 token 或用户数据时,清洗 URLs。
- 除非明确需要 debug collection,否则 console capture 只采集 `warn` 和 `error`。
- 使用稳定 tags,例如 `service`、`env`、`runtime`;把高基数字段放进 event data,而不是 tags。
示例:
```ts
import { captureFetchIntegration } from "@loggerjs/browser";
import { redactProcessor } from "@loggerjs/processors";
const processors = [redactProcessor({ keys: ["password", "token", /secret/i] })];
const integrations = [
captureFetchIntegration({
captureRequestHeaders: ["content-type", "x-request-id"],
captureResponseHeaders: ["content-type", "x-request-id"],
sanitizeUrl: (url) => new URL(url, location.origin).origin,
}),
];
```
`redactProcessor()` 可按 key、精确 dot path、regex 或自定义 matcher mask。Paths 相对于每个被脱敏的 event field(`user.password`,不是 `data.user.password`)。用 `replacement`(或兼容 Pino 的 `censor` 别名)mask 值;字段应从 event 中省略时使用 `remove: true`。LoggerJS 不用 `eval` 或 `new Function` 编译 redaction paths;类似 wildcard 的 regex 和深度遍历比生成代码更安全,但成本高于精确 keys 和 paths。
`privacyGuardProcessor()` 更宽泛:它扫描选定字段中的内置和自定义字符串 patterns,例如 emails、bearer tokens 和类似银行卡号的值。把它当成安全网,而不是 capture allowlists 的替代品。
## 浏览器队列和离线重放
`browserHttpTransport()` 会在内存中批量 records,并能把失败 payload 持久化到 offline queue adapter。内置 memory queue 有意保持小而零依赖;需要 reload survival 的应用应提供遵循相同接口的 IndexedDB-backed adapter。
```ts
import { browserHttpTransport, memoryBrowserHttpOfflineQueue } from "@loggerjs/browser";
const transport = browserHttpTransport({
url: "/api/logs",
maxBatchSize: 50,
maxQueueSize: 1000,
offlineQueue: memoryBrowserHttpOfflineQueue({ maxEntries: 500 }),
useBeaconOnPageHide: true,
beaconMaxBytes: 60 * 1024,
});
```
浏览器触发 `online` 时,transport 会带 retry 和 backoff 重放已存 payload。关闭 tab 或导航时日志很重要,就启用 page lifecycle integration:
```ts
import { pageLifecycleIntegration } from "@loggerjs/browser";
const integrations = [pageLifecycleIntegration()];
```
浏览器存储和关闭行为仍是 best effort。`sendBeacon` 可能受大小限制或在 shutdown 中被跳过;内存队列会在 reload 后消失;IndexedDB 可能不可用、满、被驱逐,或被 upgrade 阻塞。生产浏览器投递建议组合:
- `browserHttpTransport()`:常规远程投递。
- `indexedDbBrowserHttpOfflineQueue()` 或 `offlineFirstTransport()`:reload-surviving replay。
- `pageLifecycleIntegration()` 和 `useBeaconOnPageHide`:最后机会 flush。
- logger meta 中的 drop/queue metrics:让 quota 或 backpressure 可见。
## Node 崩溃路径
进程级失败建议组合 `captureProcessIntegration()` 和至少一个能在需要时同步 flush 的 transport。
```ts
import { captureProcessIntegration, fileTransport, stdoutTransport } from "@loggerjs/node";
const transports = [
stdoutTransport(),
fileTransport({ path: "./logs/app.ndjson" }),
];
const integrations = [captureProcessIntegration({ exitOnUncaught: true })];
```
崩溃路径建议:
- fatal process events 至少保留一个本地 transport。
- transport 支持时,最终同步 shutdown 优先用 `flushSync()`;普通 drain-and-continue shutdown 使用 `await flush()`。
- 每次写入都必须在日志调用返回前到达文件系统时,使用 `fileTransport({ sync: true })`。
- HTTP/OTLP remote transports 用于常规投递,不要作为唯一 fatal-path sink。
- processor 工作保持同步且有界;crash handlers 不应执行慢 enrichment。
对带 `exitOnUncaught: true` 的 `uncaughtException`,流程是:
1. 捕获一条带 `process.kind: "uncaughtException"` 的 `fatal` record。
2. 对支持同步的 transports 调用 `flushSync()`。
3. 运行一次由 `flushTimeoutMs` 控制的有界 async `flush()` race(默认 `250` ms)。
4. 以 code `1` 退出。
对带 `exitOnSignal: true` 的 signals,LoggerJS 捕获 fatal signal record,使用相同的 sync-plus-bounded-async flush 流程,然后按已知 signal exit code 退出(`SIGTERM` -> `143`,`SIGINT` -> `130`)。
## 远程 Transport 可靠性
基于 batch 的 transports 支持同一套 core reliability 选项:
```ts
{
maxRecords: 100,
maxBytes: 64 * 1024,
maxWaitMs: 2000,
concurrency: 2,
maxRetries: 3,
retryBaseDelayMs: 250,
retryMaxDelayMs: 5000,
circuitBreakerFailureThreshold: 5,
circuitBreakerResetMs: 30000,
}
```
当 payload size 比 event count 更重要时使用 byte limits。使用 `onDrop` 把 queue drops 暴露到你自己的 metrics pipeline。
## Context 和 Trace 关联
构造时已知的值用显式 child context;request 级值用 ambient context:
```ts
import { installAsyncLocalStorageContext } from "@loggerjs/node";
import { withContext } from "@loggerjs/core";
installAsyncLocalStorageContext();
await withContext({ requestId: "req_123" }, async () => {
logger.info("request started");
});
```
当 OpenTelemetry API object 可用时,用 `openTelemetryTraceProcessor()` 附加当前 active span context。
## 相关链接
- [英文原文](/OPERATIONS)
- [中文首页](/zh/)
---
# 性能
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/PERFORMANCE.md
---
title: "性能"
description: "热路径调优、批量、codec 选择和性能护栏。"
---
# 性能指南
本页是面向用户的性能说明,与 [基准](BENCHMARKS.md)(测量数字)、[基准矩阵](BENCHMARK-MATRIX.md)(签入的机器证据)和 [架构](ARCHITECTURE.md) 的性能预算部分(目标和决策)配套。它说明如何按吞吐量配置 LoggerJS,以及哪些习惯能让 hot path 保持 hot。
参考数字(Apple M1 Max,Node v22.21.1;方法见 [基准](BENCHMARKS.md),签入行见 [基准矩阵](BENCHMARK-MATRIX.md))。loggerjs-vs-pino 数字来自 paired A/B harness;相对 pino 的排序依赖 CPU/Node-V8,请用 `BENCH_AB=1 pnpm bench:node` 复现:
| Path | Cost |
| --- | ---: |
| Disabled level call | ~3 ns(pino parity) |
| Enabled pipeline, record fast path, noop sink | ~83 ns |
| Batch transport enqueue(默认设置) | ~172 ns |
| Prepared lean NDJSON line to a sink | ~224 ns(1.28x pino) |
| Lean NDJSON line to a sink | ~242 ns(1.19x pino) |
| Full NDJSON line with id/seq/levelName | ~307 ns |
## 免费收益(默认已经这样做)
- **Disabled levels 只花一次比较。** 保留代码里的 `trace`/`debug` 调用,用 `level` gate。
- **Lazy messages** 只在级别启用时求值,且最多一次:`logger.debug(() => expensive())`。
- **Logger tags 冻结并跨 records 共享**,没有 per-call copy。
- **Default ids** 会按毫秒 memoize timestamp segment。
- **Batch byte estimation** 只有设置有限 `maxBytes` 时才执行。
- **`ndjsonCodec` 默认走 native fast path**,并为会抛错的输入提供 safe fallback。
## Record Fast Path
这是最大的配置杠杆。当 logger 有 **零 processors**,且 transports 是 **record-aware**(`write`/`writeBatch`)时,不会构建 `LogEvent`:没有 id factory、没有 message-error projection、没有第二个对象。
```ts
// Fast path: middleware + record-aware transport
createLogger({
middleware: [tagsMiddleware({ service: "checkout" })], // middleware 保留 fast path
transports: [recordAwareTransport],
});
// 离开 fast path: 任意 processor 都会强制每条日志做 event projection
createLogger({
processors: [sampleProcessor()],
transports: [recordAwareTransport],
});
```
实践建议:
- 当 middleware variants 和 processor twins 同时存在时,优先使用 `tagsMiddleware`、`enrichMiddleware`、`traceContextMiddleware` 等 middleware 版本。
- Processors 对 event-shape 行为仍是正确工具(routing、fingerprinting、fingers-crossed)。需要它们时接受 projection 成本:大约 100ns,不是灾难。
## Codec 选择
- 最高吞吐:`@loggerjs/codecs` 的 `fastEventJsonCodec()`;下游不需要 `id`/`seq`/`levelName` 时可使用 lean envelope。
- `ndjsonCodec()`(stdout 默认)在 event path 上与 fast-event-json 差距约 10%。
- Prepared record encoders 适合自定义 sinks。Record-aware transport 直接写 codec 时,用 `createPreparedRecordEncoder(codec)` 包装一次,让 codec-owned logger/tag fragments 可复用,而不把 serialization 移入 logger。
- `safeJsonCodec()` 每条日志都付完整 normalization walk;把它用于经常有 hostile payloads 的场景,而不是最高吞吐路径。
- 自定义 `idFactory`(UUID 等)会付 per-log 成本;默认 id 接近免费且可排序。
## 远程目的地的 Batching
Per-event network calls 是现实中的主要成本;这里每个 remote transport 都基于 `batchTransport`:
- `maxRecords` / `maxWaitMs` 在 latency 和 batch size 间取舍;默认(50 / 2000ms)适合大多数服务。
- 只有目的地强制 payload limits 时才设置 `maxBytes`,因为启用它会打开 per-log byte estimation。
- `concurrency: 2..4` 可重叠慢 endpoint round trips。
- 观察 `getLoggerMetaStats()` 中的 `transport.dropped.*`;drops 表示 queue bound 与流量不匹配。
## 会伤害性能的习惯
- **Middleware/processors 中的重同步工作。** 管线设计为同步;1ms enrichment 会让每条日志多 1ms。
- **在管线中提前 stringify。** 序列化属于 transport codec;字符串 blob 也会破坏 redaction。
- **所有日志都走一个共享 catch-all logger 并挂很多 processors**,但只有一条 route 需要它们。按目的拆分 loggers;children 很便宜。
- **无界 data payloads。** 编码成本与 payload size 成正比;记录 identifiers,不要记录整块实体。
## Import Boundaries
Root `@loggerjs/browser` 和 `@loggerjs/node` 入口是 preset-style convenience imports:它们 re-export core 加所有第一方 runtime transports 和 integrations。当应用简洁性比最小 module graph 更重要时使用它们。
更小 bundle 使用文档化 subpaths。Browser 和 Node subpaths 构建为真实物理 entry bundles,并由 `pnpm verify:entry-boundaries` 验证,因此 focused import 不会指回 aggregate `dist/index`:
```ts
import { browserHttpTransport } from "@loggerjs/browser/transport-http";
import { captureFetchIntegration } from "@loggerjs/browser/integration-fetch";
import { stdoutTransport } from "@loggerjs/node/transport-stdout";
```
新增 runtime-specific feature 且不属于 common preset path 时,应放在 subpath entry 后面。如果新功能让 root browser/node bundle 变大,size-budget diff 应解释为什么 preset entry 需要它。
## 护栏
性能在 CI 中有门禁:`pnpm bench:gate` 运行 interleaved A/B suites,并用与匹配 pino baseline 的 paired ratios 强制限制(见 BENCHMARKS.md)。贡献 hot path 变更时本地运行;结构性回归会让 pull request 失败。
优化的有意终态记录在 ARCHITECTURE.md:保留共享 `LogRecord` 管线作为默认架构,但允许 codec/transport-owned preparation 复用稳定片段。绕过 record 的 fusion paths 仍不作为默认方案,因为它们会制造独立语义 hot path。
## 相关链接
- [英文原文](/PERFORMANCE)
- [中文首页](/zh/)
---
# 基准
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/BENCHMARKS.md
---
title: "基准"
description: "基准方法、命令和当前机器快照。"
---
# 基准
LoggerJS 基准有意保持简单且可复现。它们测量来自 `dist` 的 public package builds,而不是 TypeScript 源码。
## 命令
```bash
pnpm bench
pnpm bench:node
pnpm bench:browser
pnpm bench:gate
pnpm bench:matrix -- --runs=5 --rounds=120 --label="$(hostname)-node22"
pnpm bench:matrix:aggregate -- benchmarks/matrix --out docs/BENCHMARK-MATRIX.md
pnpm size:check
```
`pnpm bench:gate` 运行 interleaved A/B suites,并用与匹配 pino baseline 的 paired per-round ratios 强制 regression limits。它使用与 `BENCH_AB` 相同的 drift-canceling 方法,因此 CPU frequency、scheduler placement 和 GC pauses 会在同一 round 中影响两个 contender。Limits 在 `scripts/check-bench-regression.mjs` 中,且故意宽松:抓结构性回归,而不是噪声。CI 在每个 pull request 上运行这个 gate。可用 `BENCH_GATE_AB_ROUNDS`、`BENCH_GATE_AB_BATCH` 和 `BENCH_GATE_AB_WARMUP` 调节。
`pnpm bench` 会先构建 workspace,再运行 Node 和 browser benchmarks。Browser benchmarks 使用本地 headless Chrome binary。Chrome 不在标准位置时设置 `CHROME_BIN`。
### Apples-to-apples cross-logger ratios(`BENCH_AB`)
普通套件会在一次运行中于不同时间点分别测每个 logger,因此 loggerjs-vs-pino ratio 会随 CPU frequency scaling 和 P/E-core scheduling 漂移;单次顺序运行可能只因为“被测时间点”而让任一 logger 看起来更好。公平比较两个 logger 时,使用 interleaved A/B mode:
```bash
BENCH_AB=1 node scripts/bench-node.mjs
# tune: BENCH_AB_ROUNDS (default 60), BENCH_AB_BATCH (5000), BENCH_AB_WARMUP (100000)
BENCH_AB=1 BENCH_JSON=1 node scripts/bench-node.mjs # machine-readable
```
每个 round 都让 selected suite 中的 contenders **背靠背**计时,并轮换起始位置,让 drift 平等影响双方并在 **paired per-round ratio** 中抵消。默认 suite 比较 pino、lean、prepared 和 full-envelope record sink;`BENCH_AB_SUITE=disabled` 和 `BENCH_AB_SUITE=enqueue` 由 CI gate 使用。报告输出每个 contender 的 ns/op,以及 median ratio 和 min/max spread;当 baseline spread 超过 25% 时发出 warning,表示机器太吵,absolute ns 不可靠(ratio 仍保持公平)。跨 logger ratio 只引用此模式并配稳定 baseline,不要引用单次顺序运行。
### 跨机器基准矩阵
当你需要支撑更强的表述,例如“LoggerJS 在我们测试过的每台机器上都快于 pino”,收集多个本地 A/B artifacts 并聚合:
```bash
pnpm build
pnpm bench:matrix -- --runs=5 --rounds=120 --label="$(hostname)-node22"
# after copying artifacts from other machines into benchmarks/matrix/
pnpm bench:matrix:aggregate -- benchmarks/matrix --out docs/BENCHMARK-MATRIX.md
```
`pnpm bench:matrix` 包装 `BENCH_AB=1 BENCH_JSON=1` harness,运行多次,记录 CPU/OS/Node/dependency/Git metadata,并默认在 `benchmarks/matrix/` 下写入 JSON 和 Markdown artifacts。该目录被忽略,因为它是本地证据。只提交经过有意整理的 aggregate,例如 [BENCHMARK-MATRIX.md](BENCHMARK-MATRIX.md)。做跨机器性能表述时,应引用签入的 matrix evidence file。
非 Apple-Silicon 和多 Node 证据可运行手动 GitHub Actions workflow `Benchmark Matrix`。它会为 Node 20.19.0、22 和 24 采集 Linux x64 rows,并上传 aggregate Markdown artifact 供 review。只有在核对 JSON artifacts 和 runner metadata 后才提交 aggregate。
谨慎使用矩阵措辞:它只能证明列出的 machine/runtime/dependency combinations,不能证明未来所有 CPU、Node/V8 版本或 pino releases 的普遍结果。
## Node 场景
- 带 lazy message 的 disabled debug log。
- 没有 transports 的 enabled logger。
- 使用 no-op transport 的 enabled logger。
- 使用 record-aware no-op write transport 的 enabled logger(record fast path,无 event projection)。
- 使用 no-op patched console 的 console transport。
- Batch transport enqueue path。
- 与 pino、winston、LogTape 和 Node console 的 full-path NDJSON 比较。
- JSON、safe JSON、fast event JSON 和 msgpackr encode/decode。
- Fast event JSON 编码 raw LogRecord batches(record transport boundary)。
## 竞争者比较
Full-path 场景每次迭代记录一条 structured info call,并把序列化行交给 discarding sink,因此比较的是 pipeline + serialization,不含 terminal 或 filesystem I/O 噪声。pino、winston 和 LogTape 是 root lockfile 中固定的 dev dependencies。Node console 场景使用一个真实 `Console` 实例,背后是 discarding stream。
参考机器:**Apple M1 Max(64 GB),Node v22.21.1**,pino 10.3.1、winston 3.19.0、LogTape 2.1.3。loggerjs-vs-pino 行来自 drift-canceling paired A/B harness(`BENCH_AB`,22 runs x 120 rounds);更广泛 landscape 来自单次 `BENCH_ITERATIONS=1000000` 顺序运行。
### Cross-logger comparison(paired A/B,可信方法)
每个 round 都背靠背测 pino、lean 和 prepared,因此 CPU frequency 与 core scheduling 平等影响它们并在 ratio 中抵消。22 runs 的 medians:
| Path | ns/op | vs pino |
| --- | ---: | --- |
| pino ndjson noop sink | 287 | 1.00x baseline |
| loggerjs lean record sink | 242 | **1.19x pino**(paired ratio 0.84,range 0.82-0.87) |
| loggerjs prepared lean record sink | 224 | **1.28x pino**(paired ratio 0.78) |
在这台机器上,loggerjs lean 和 prepared 在等价输出下 **快于 pino**,且可复现:22 次运行里 lean/pino paired ratio 都保持在 0.84 +/- 0.02,即使某些 round 的 GC pause 把 absolute spread 推到 80% 以上也成立。Prepared encoder 比 plain lean 快约 8%。
**这个排序依赖环境。** pino 和 loggerjs 都使用手调 JSON hot paths,CPU、scheduler、Node/V8 的细微差异都可能改变胜者。上表是所列参考机器的经验结果,不是机制声明或普遍排名。请始终在自己的硬件上复现:`BENCH_AB=1 pnpm bench:node`,再用 `pnpm bench:matrix` 增加持久证据行。
### 顺序套件(同机单次 1,000,000 次迭代)
每个场景的绝对吞吐。这里的跨 logger ratios **不可靠**(各 logger 在运行中的不同时间点被测);loggerjs-vs-pino 使用上方 A/B 表。本表用于数量级 landscape 和 codec paths。
| Scenario | ns/op |
| --- | ---: |
| loggerjs disabled debug(lazy message) | 3 |
| pino disabled debug | 9 |
| loggerjs batch transport enqueue | 172 |
| loggerjs prepared lean record sink | 252 |
| loggerjs lean record sink | 273 |
| loggerjs full-envelope record sink(`+id/seq/levelName`) | 307 |
| loggerjs ndjson event sink | 812 |
| loggerjs fast-event-json event sink | 897 |
| node console info noop stream | 769 |
| winston json noop sink | 2,726 |
| logtape json lines noop sink | 6,584 |
所有 loggerjs 和 pino full-path loggers 都带相同 base fields(`service`、`env`)。Lean sink 使用 `fastEventJsonCodec({ includeId: false, includeSeq: false, includeLevelName: false })`;prepared lean sink 用 `createPreparedRecordEncoder(codec)` 包装它,复用 codec-owned logger/tags fragments,而不把 serialization 移入 logger;full-envelope sink 额外输出 `id`、`seq` 和 `levelName`。CI 强制的是 `pnpm bench:gate` 中的 **paired A/B ratios**(默认每个 contender 60 rounds x 5000 ops),覆盖 disabled-level logging、record-write enqueue、batch enqueue、lean、prepared 和 full-envelope record sinks。
诚实解读:
- Disabled-level logging 与 pino 同级(都是个位数 ns)。
- 对等 lean output 下,loggerjs 在 M1 Max 参考机器上 **快于 pino**(paired A/B,lean 1.19x / prepared 1.28x),但排序依赖 CPU/V8;应表述为“pino 同级,机器相关胜者”,不是普遍声明。Prepared encoder 额外约 8%。
- Full-envelope path 比 lean 多约 13% 成本,以携带 `id`、`seq` 和 `levelName`;下游不需要这些字段时选择 lean envelope。
- loggerjs 大约比 winston 快一个数量级(约 10x)、比 LogTape 快约 24x、比 Node console 快约 3x;这些倍数会随系统负载摆动,作为近似理解。
- 早期快照中 pino 在 mixed suite 里是 442ns,那是 JIT warmup artifact(10k warmup iterations),后来通过让每个场景 warmup 为 measured iterations 的四分之一修复。除非 warmup 与迭代数成比例,否则跨 logger 比较无效。
热路径变更后重新运行 `pnpm bench:node`,数字有实质变化时更新快照。
调节迭代次数:
```bash
BENCH_ITERATIONS=200000 pnpm bench:node
BENCH_BROWSER_ITERATIONS=100000 pnpm bench:browser
BENCH_BROWSER_IDB_ITERATIONS=5000 pnpm bench:browser
```
## 浏览器场景
`pnpm bench:browser` 在本地 headless Chrome 中运行,并测量来自构建后 `dist` packages 的 browser-facing paths:
- 没有 transports 的 enabled browser logger。
- 使用 no-op `fetchFn` 的 Browser HTTP transport enqueue。
- IndexedDB transport enqueue 到 in-memory transport buffer。
- 浏览器 batches 的 JSON 和 fast event JSON encoding。
- IndexedDB transport flush 一个 persisted batch。
- IndexedDB HTTP offline queue enqueue。
IndexedDB 场景使用单独迭代数,因为它们会执行真实浏览器 storage I/O。通过 `BENCH_BROWSER_IDB_ITERATIONS` 调整;默认值故意小于 `BENCH_BROWSER_ITERATIONS`,保证日常 browser benchmark 运行够快。Browser storage 数字对 Chrome 版本、profile state、设备存储、private browsing policy、quota 和 Storage Buckets 支持敏感,引用时必须带测量浏览器和硬件上下文。
## Size Budgets
`pnpm size:check` 在 build 后运行,并对每个 package entry bundle 强制 raw + gzip budgets。Budgets 存在 `scripts/check-size-budgets.mjs` 中,只应随有意 public surface 或 implementation-size 变更一起更新。
## 相关链接
- [英文原文](/BENCHMARKS)
- [中文首页](/zh/)
---
# 基准矩阵
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/BENCHMARK-MATRIX.md
---
title: "基准矩阵"
description: "跨机器基准证据和记录方式。"
---
# LoggerJS 基准矩阵
最后更新:2026-06-18(两台机器的 rows 手工合并;每台机器的 JSON artifacts 位于 gitignored `benchmarks/matrix/`,所以 aggregate command 不能重新生成本地不存在 artifact 的 row)。
这张表是签入仓库的 loggerjs-vs-pino Node hot-path 声明证据矩阵。Ratios 来自 interleaved A/B harness 的 paired per-round latency medians,不是一轮顺序运行的 ratio。低于 `1.00x` 表示该机器上 LoggerJS path latency 低于 pino。**下面两行有意不一致,这正是重点:**排序依赖 CPU/V8(M1 Max 上 LoggerJS 更快,M4 Pro 上 pino 更快),因此不能支持普遍“快于 pino”的声明。
| Label | Platform | CPU | Node | Git | Runs | Pino ns | Lean ns | Prepared ns | Lean / pino | Prepared / pino | Result |
| --- | --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| macbookpro-node22 | darwin/arm64 | Apple M1 Max | v22.21.1 | 5d7e4e3bf423 | 5 | 286 | 244 | 223 | 0.843x (118.6%) | 0.773x (129.3%) | LoggerJS lean + prepared faster |
| m4pro-node22 | darwin/arm64 | Apple M4 Pro | v22.22.2 | 1d3d51c21f86 | 6 | 197 | 223 | 211 | 1.137x (87.9%) | 1.054x (94.9%) | pino faster (lean + prepared) |
## Row Details
| Label | Memory | Dependencies | Sampling | Baseline spread | Prepared / lean |
| --- | ---: | --- | --- | ---: | ---: |
| macbookpro-node22 | 64 GB | pino 10.3.1, winston 3.19.0, LogTape 2.1.3 | 5 runs, 120 rounds x 5000 ops, 100000 warmup | 21.2% | 0.919x (108.8%) |
| m4pro-node22 | 24 GB | pino 10.3.1, winston 3.19.0, LogTape 2.1.5 | 6 runs, 120 rounds x 5000 ops, 100000 warmup | 41.9% | 0.942x (106.1%) |
## Evidence Coverage
| Requirement | Status | Rows |
| --- | --- | --- |
| At least one non-Apple-Silicon runtime | Missing | darwin/arm64 |
| At least two Node major versions | Missing | 22 |
两个 gate 仍然 **Missing**:M4 Pro row 是 Node 22 上的第二台 Apple-Silicon CPU,不满足 non-Apple 或 second-Node-major 要求。不过它已经把 universal-claim guard 具体化:在同一 OS/arch/Node-major 内,只替换 CPU(M1 Max -> M4 Pro)就能把结果从“LoggerJS faster”翻转为“pino faster”。在加入 non-Apple 和 second Node-major row 前,性能措辞必须限定到 CPU 范围,不要写成“LoggerJS 总是快于 pino”。
## 复现
```bash
pnpm build
pnpm bench:matrix -- --runs=5 --rounds=120 --label="$(hostname)-node22"
# after copying artifacts from other machines into benchmarks/matrix/
pnpm bench:matrix:aggregate -- benchmarks/matrix --out docs/BENCHMARK-MATRIX.md
```
非 Apple-Silicon 和多 Node 证据运行手动 GitHub Actions workflow:
```bash
gh workflow run benchmark-matrix.yml -f runs=5 -f rounds=120 -f batch=5000 -f warmup=100000
```
下载 `benchmark-matrix-aggregate` artifact,review 生成的 `benchmark-matrix-ci.md`,并且只有当 rows 来自预期 machine/runtime combinations 时才提交到本文件。不要在没有匹配 JSON artifacts 的情况下手写 benchmark rows。
说明:
- 矩阵只证明列出的 machine/runtime/dependency combinations。不要把它扩展成普遍“always faster than pino”。
- 测试新 CPUs、操作系统、Node/V8 versions 或 pino releases 时新增 rows。
- 如果 row 来自 dirty worktree,在 Git 列中标记 `*`。
## 相关链接
- [英文原文](/BENCHMARK-MATRIX)
- [中文首页](/zh/)
---
# 对比
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/COMPARISON.md
---
title: "对比"
description: "LoggerJS 与其他 JavaScript logger 的定位对比。"
---
# LoggerJS 与其他 JavaScript Logger 对比
本页比较当前 LoggerJS workspace 与常见 JavaScript logging libraries。它基于当前仓库状态,而不是目标路线图。
## 范围
除非表格单元格明确写着 “ecosystem”,否则比较使用 first-party 行为。来源检查时间为 2026-06-12:
- LoggerJS 仓库文档:[README](https://github.com/jskits/loggerjs/blob/main/README.md)、[核心概念](CONCEPTS.md)、[传输](TRANSPORTS.md)、[集成](INTEGRATIONS.md)、[处理器](PROCESSORS.md)、[编解码](CODECS.md)、[基准](BENCHMARKS.md)。
- Pino 官方文档:、、、、。
- Winston 官方 README:。
- LogTape 官方文档和 JSR package page:、、、、。
- Bunyan 官方 README:。
- 轻量和 developer-experience 工具:、、、。
下方 benchmark 数字只适用于 [基准](BENCHMARKS.md) 中的场景。它们不声称在所有 sinks、runtimes、payload shapes 或第三方 transports 上都有普遍优势。
## 简短结论
当日志问题横跨浏览器和服务器采集时,LoggerJS 最合适:automatic integrations、structured middleware、可靠 transport delivery、浏览器离线持久化、每个 destination 自选 codec,以及从同一心智模型投递到 vendor/DB/OTLP。它最可防御的细分场景是 **vendor-neutral, self-hosted delivery**:日志发送到你自己拥有的目的地(HTTP、files、your DB、Loki/Elasticsearch、OTLP),来自一个能在 strict CSP、edge/Workers、offline 场景中运行的 zero-dependency core。对于这些场景,纯 Node logger 或 managed APM SaaS 往往不够贴合。
如果主要需求是最小、Node-first、生态成熟的 JSON logger,Pino 仍是成熟默认选择。在当前 M1 Max 参考 benchmark 上,LoggerJS 等价 lean/prepared paths 更快,但排序依赖 CPU/Node-V8。Winston 仍是成熟、灵活的 Node transport 和 format ecosystem。LogTape 是最接近的架构同行,适合 library-first usage 和 multi-runtime categories。Bunyan 是稳定的 legacy Node JSON logger。
## 一览
图例:✅ first-party fit,🧩 ecosystem fit,⚠️ partial 或依赖配置,❌ checked first-party equivalent 不存在,📊 本仓库测量。
| 能力 | LoggerJS | Pino | Winston | LogTape | Bunyan |
| --- | --- | --- | --- | --- | --- |
| Node server logging | ✅ first-party | ✅ first-party | ✅ first-party | ✅ first-party | ✅ first-party |
| Browser runtime | ✅ first-party | ⚠️ browser API | ⚠️ not primary | ✅ first-party | ⚠️ bundler support |
| Library-safe default | ✅ silent until configured | ⚠️ app-oriented | ⚠️ app-oriented | ✅ core design | ⚠️ app-oriented |
| Automatic browser capture | ✅ 19 first-party integrations | ❌ none checked | ❌ none checked | ❌ none checked | ❌ none checked |
| Automatic Node collection | ✅ 16 first-party integrations | 🧩 ecosystem | ⚠️ exceptions/rejections | ✅ framework packages | ⚠️ stream/custom |
| Multi-destination delivery | ✅ transports | ✅ transports | ✅ transports | ✅ sinks | ✅ streams |
| Built-in batching/retry/offline | ✅ shared primitives | ⚠️ transport-dependent | ⚠️ transport-dependent | ⚠️ sink-dependent | ⚠️ stream-dependent |
| Transport-owned codecs | ✅ explicit boundary | ⚠️ logger/transport formatting | ⚠️ format pipeline | ⚠️ sink formatting | ⚠️ serializers |
| Privacy/redaction | ✅ processors + sanitizers | ✅ built-in redaction | ⚠️ custom formats | ✅ redaction package | ⚠️ serializers/custom |
| Direct Node JSON path | ✅ M1 上 1.19x pino | 📊 baseline(同级;其他 CPU/V8 上可领先) | ❌ measured slower | ❌ measured slower | Not measured here |
## 详细矩阵
| 维度 | LoggerJS | Pino | Winston | LogTape | Bunyan |
| --- | --- | --- | --- | --- | --- |
| Primary fit | 带 automatic collection 和可靠 delivery 的同构结构化日志 | 低开销 Node JSON logging | 成熟 format/transport model 的灵活 Node logger | 跨 JS runtimes 的 library-first structured logging | Node services 的简单 JSON logging |
| Runtime posture | `@loggerjs/core` 平台中立;first-party Node 和 browser packages 按 runtime 拆分 | Node-first,带文档化 browser API | Node-first;browser 不是主要文档路径 | first-party 支持 Node、Deno、Bun、browsers、Cloudflare Workers 和 edge | Node services;文档提到 Browserify/Webpack/NW.js support |
| Library-safe default | 是:`getLogger()` 在宿主配置前保持 silent | 部分:库可以接收/注入 logger,但 Pino 本身偏 app-oriented | 部分:库可以接收/注入 logger,但 Winston 本身偏 app-oriented | 是:core design goal | 部分:child loggers 有帮助,但预期 app-level configuration |
| Structured data | 是:records/events 保留 message、data、context、tags、trace、source、type | 是:默认 JSON logs | 是:mutable `info` objects 加 formats | 是:structured log records/properties | 是:JSON records |
| Levels | 兼容 Pino 的 numeric levels 加 names | 内置 numeric levels 和 custom levels | RFC5424-style levels 加 custom levels | 带 category configuration 的 severity levels | Numeric levels |
| Category/logger model | Category arrays、child loggers、registry configuration | Child loggers 和 bindings | Logger instances、child loggers、containers | 带 sink inheritance 的 hierarchical categories | Logger name 加 child loggers;文档说 names 非层级 |
| Middleware/filter layer | first-party middleware/processors:enrich、redact、sample、dedupe、route、rate-limit、fingerprint、normalize | Hooks、serializers、mixins、redaction;更广 middleware 通常是 app/ecosystem code | Format chains 和 custom formats;mutable object pipeline | Filters、contexts、formatters、redaction package | Serializers 和 custom streams |
| Serialization ownership | Codec 属于每个 transport;内置 JSON、safe JSON、NDJSON、fast-event-json、msgpackr、OTLP JSON | Core JSON serialization 加 serializers/formatters 和 transport output | Format chain 为每个 logger/transport 最终化输出 | Sinks 和 formatters 拥有输出 | JSON records 加 serializers |
| Transport/sink model | first-party console、pretty DevTools/terminal output、memory、test、batch、stdout/stderr、file、rotating file、HTTP、syslog、worker、browser HTTP、IndexedDB、WebSocket、service worker、BroadcastChannel、OTLP、Sentry、Datadog、Elasticsearch、Loki、CloudWatch、SQLite/Postgres/custom DB | Destination/transport API、multi-target transports、`pino/file`、`pino-pretty` 和 ecosystem transports | 内置 console/file/http/stream-style transports 和广泛 custom transport ecosystem | Core 中的 console/stream sinks,以及 file、OTEL、Sentry、syslog、CloudWatch、Windows Event Log 等 packages | stdout/file/rotation/raw/custom streams |
| Automatic browser collection | 19 个 first-party browser/frontend integrations:console、script/resource errors、unhandled rejection、fetch、XHR、Web Vitals、performance entries、user actions、router adapters、ReportingObserver、service worker、WebSocket、framework error hooks、runtime host、browser context propagation | Browser API 可直接 logging;checked docs 未发现等价 LoggerJS browser capture suite | checked docs 未发现等价 LoggerJS browser capture suite | 支持 browser runtime;checked docs 未显示等价 browser capture/offline suite | checked docs 未发现等价能力 |
| Automatic Node collection | 16 个 first-party Node.js/server integrations:process、diagnostics_channel、Express、Fastify、Koa、Hapi、Nest middleware、fetch、http client、CLI、serverless、queue、BullMQ、Prisma、Redis、generic DB clients | Fastify/Pino、pino-http 等 ecosystem integrations 常见;core docs 覆盖 logger/transports | 内置 uncaught exception 和 unhandled rejection handling;framework request logging 通常是 ecosystem code | first-party framework integration packages 包括 Express、Fastify、Hono、Koa 和 Drizzle | checked docs 未显示广泛 first-party instrumentation suite |
| Browser persistence/export | first-party IndexedDB transport、IndexedDB HTTP offline queue、pagehide flush、ZIP export | checked docs 未发现 first-party equivalent | checked docs 未发现 first-party equivalent | checked core docs 未发现 first-party equivalent | checked docs 未发现 first-party equivalent |
| Delivery reliability | 共享 batching、retry/backoff、byte limits、circuit breaker、flush/flushSync/close、适用时 offline queues | 高吞吐 stream/transport model;transport startup caveats 有文档 | Transport model 带 exceptions/rejections、querying、streaming、close/await guidance | Sink model 带 category/filter/context control;reliability 取决于 chosen sink packages | Stream model;reliability 取决于 chosen streams |
| Privacy controls | Redaction、privacy guard、normalize-error、safe codecs、integration 中 URL/header sanitizers | 使用 fast-redact 的内置 path redaction | Formatting 和 custom transforms;checked README 中无 built-in redaction claim | Redaction package 和 filters | Serializers/custom streams |
| Context propagation | Child loggers、bindings、tags、`withContext()`、Node AsyncLocalStorage installer | Child loggers、bindings、mixins;async context 是 app/ecosystem code | Child logger metadata;async context 是 app/ecosystem code | Explicit 和 implicit contexts,带 configurable context local storage | Child loggers 和 serializers |
| TypeScript posture | first-party TypeScript source/declarations、typed events、subpath exports | Types included in package ecosystem | Types included in package ecosystem | TypeScript-first package | 历史 Node package,带 TypeScript ecosystem support |
| Dependency posture | `@loggerjs/core` 无 dependencies;完整 workspace packages 只增加目标依赖,例如 `@loggerjs/codecs` 中的 `msgpackr` | 小 core,加 focused dependencies | 成熟但更大的 dependency graph | `@logtape/logtape` zero dependencies | 较老 package,某些功能有 optional deps |
## 性能快照
当前测量快照来自 [基准](BENCHMARKS.md) 和签入的 [基准矩阵](BENCHMARK-MATRIX.md):参考机器 Apple M1 Max(64 GB)、Node v22.21.1、pino 10.3.1、winston 3.19.0、LogTape 2.1.3。loggerjs-vs-pino 行来自 drift-canceling paired A/B(22 runs);竞争者 landscape 行来自顺序套件:
| Scenario | ns/op | 解读 |
| --- | ---: | --- |
| loggerjs disabled debug, lazy message | 3 | Disabled level path 与 pino 同级 |
| pino disabled debug | 9 | 同一等级开销 |
| loggerjs prepared lean record sink | 224 | Codec-owned prepared encoder,paired A/B 下 1.28x pino |
| loggerjs lean record sink | 242 | `fastEventJsonCodec` lean JSON,paired A/B 下 1.19x pino |
| pino NDJSON noop sink | 287 | Direct JSON path;baseline |
| loggerjs full-envelope record sink | 307 | 额外 `id`、`seq` 和 `levelName`,约 0.9x pino |
| node console info noop stream | 769 | 比 loggerjs lean sink 慢约 3x |
| winston JSON noop sink | 2,726 | 比 loggerjs lean sink 慢约 11x |
| LogTape JSON lines noop sink | 6,584 | 比 loggerjs lean sink 慢约 27x |
诚实解读:
- 在 M1 Max 参考机器上,LoggerJS lean 和 prepared 在等价输出下 **快于 Pino**(1.19x / 1.28x,paired A/B,22 runs 可复现)。这 **不是** 普遍“beats Pino”声明:排序依赖 CPU/Node-V8,文档把差异当成经验 benchmark 结果,而不是已证明机制。请在你的硬件上用 `BENCH_AB=1 pnpm bench:node` 复现,并用 `pnpm bench:matrix` 增加持久跨机器证据。
- LoggerJS 在等价输出上达到 Pino 同级,**没有** 放弃自己的 record pipeline。这个 pipeline 是设计目标,不是意外 overhead。
- Record pipeline 换来 first-class middleware、integrations、multi-transport routing、codec selection 和 browser/server symmetry。
- 这些数字没有比较每一种 Pino transport、Winston format chain、LogTape sink 或 browser scenario。
## LoggerJS 更强的地方
### 浏览器和同构应用
LoggerJS 有 first-party browser transports 和 integrations:console capture、script/resource errors、fetch/XHR failures、Web Vitals、page lifecycle flushing、router events、user actions、WebSocket lifecycle、service worker lifecycle、ReportingObserver、IndexedDB persistence、offline HTTP queues 和 ZIP export。
这是和 Pino、Winston、Bunyan 最大的实际差异。这些库可以不同程度在浏览器中使用,但 checked docs 没有显示与 LoggerJS 等价的 first-party automatic browser collection 和 local persistence suite。
### Transport-Owned Codecs
LoggerJS 在 transport 边界前保持结构化原始值。Serialization 是 transport 关注点,所以 stdout 可以用 NDJSON,browser HTTP 可以用 safe JSON 或 lean fast codec,OTLP 可以用 OTLP shape,自定义 transport 可以用 MessagePack 或 domain-specific projection。
这不同于常见 logger-level formatter model。多目的地 logging 会更少意外,因为每个目的地拥有自己的 wire contract。
### 内置可靠性 Primitives
LoggerJS 把常见投递控制作为可复用组件提供:batch transport、retry/backoff、byte limits、circuit breaker behavior、flush/close lifecycle、browser `sendBeacon`、IndexedDB offline queues,以及适用处的 transport stats。目标是:写 remote transport 时实现 destination,而不是重写 reliability layer。
### Automatic Collection 是一等概念
LoggerJS integrations 显式、可逆,并通过与手动日志相同的管线路由。捕获日志仍经过 middleware、processors、routing、codecs 和 transports。对隐私很重要,因为 redaction 和 sampling 可以集中处理。
## 什么时候其他 Logger 更合适
### 主要需求是最小 Node JSON Logging 时选 Pino
Pino 仍是低开销 Node JSON logging 的参照点,并有成熟 Node web service 生态。当前 LoggerJS paired A/B 数字让 lean/prepared 等价输出路径在 M1 Max 参考机器上领先,但这个排序依赖 CPU/Node-V8。如果应用只需要 app-authored server logs 到 stdout 或 Pino transport,Pino 仍是更简单且更久经验证的选择。
### 需要成熟 Transport/Format 生态时选 Winston
Winston 广泛、稳定、灵活。它的 `format` chain 和 transport model 在许多 Node 应用中熟悉;README 记录了 exception handling、rejection handling、profiling、querying、streaming、custom formats 和 custom transports。已有 Winston 部署只有在 LoggerJS 的同构采集、middleware model 或测得的性能收益足以抵消迁移成本时才值得迁移。
### Multi-Runtime Library-First Logging 优先时选 LogTape
对库作者而言,LogTape 是与 LoggerJS 最接近的概念同行。其官方 package page 强调 zero dependencies、library-first design、structured logging、hierarchical categories、runtime diversity、redaction 和 framework integration packages。如果 Deno/Bun/edge parity 和 core package zero dependencies 是最高优先级,LogTape 很适合。
当 first-party browser telemetry capture、IndexedDB/offline workflows、Node process/client/server integrations、transport-owned codecs,以及当前相对 pino 的 Node benchmarks 更重要时,选择 LoggerJS。
### Legacy Node JSON 兼容时选 Bunyan
如果已有服务已经输出 Bunyan-shaped JSON,或依赖 Bunyan CLI/stream ecosystem,Bunyan 仍然相关。新 browser/server 应用中,LoggerJS 内置 surface 更广。
## 其他常见工具
| 工具 | 最适合 | 与 LoggerJS 的关系 |
| --- | --- | --- |
| Native `console` | 开发输出和简单脚本 | LoggerJS 可以捕获 console calls 并路由,但 direct console 仍是最简单 debug output。它不是结构化投递管线。 |
| `loglevel` | 很小的 browser/Node console method level filtering | 小得多也简单得多。它不试图提供 transports、codecs、integrations、offline storage 或 vendor delivery。 |
| `debug` | 按 namespace、环境/local storage 开关的 debug traces | 非常适合 library debug traces。它不是结构化生产日志管线。 |
| `consola` | Pretty CLI/browser console output 和 developer tooling UX | 人类可见 console UX、tags、reporters、console redirection、prompts 很强。LoggerJS 更关注结构化 observability delivery。 |
| `tslog` | TypeScript-friendly pretty/JSON logger for Node and browser | 比 `debug` 或 `loglevel` 更接近完整 logger,并支持 attachable transports。LoggerJS 有更广的 first-party automatic collection、transport reliability 和 vendor/browser persistence surface。 |
## 迁移摩擦
LoggerJS 有意在几个地方与 Pino 和 Winston 不同:
- 普通日志使用 `(message, data)`;Pino 常用 `(object, message)`。
- 稳定 metadata 拆分到 `tags`、`bindings` 和 ambient context,而不是一个通用 `defaultMeta` 或 `base` 对象。
- 数据塑形属于 middleware/processors;序列化属于挂在 transports 上的 codecs。
- Automatic capture 是 opt-in。添加 `captureConsoleIntegration()` 或 `captureFetchIntegration()` 是显式且可逆的。
示例见 [迁移](MIGRATION.md)。
## 我们不该宣称的话
除非新增证据,否则 marketing 和 README claims 应保持在这些边界内:
- 不要宣称 LoggerJS 普遍快于 Pino。测得的 direct Node JSON 排序依赖 CPU/Node-V8;引用 benchmark matrix 中具体测试机器。
- 不要在仓库还没有对应 runtime 测试和 package metadata 前宣称完整 Deno/Bun first-party support。
- 不要宣称每个 vendor feature 都比 ecosystem plugins 更丰富。LoggerJS 有意提供常见目的地的 wire-protocol transports;成熟 vendor SDK 可能暴露更深的平台特定行为。
- 不要宣称 browser automatic collection 在所有 packages 中唯一。可支持的说法是:在 Pino、Winston、LogTape、Bunyan 的 checked docs 中,没有找到等价 first-party suite。
- 不要使用旧 benchmark snapshots。改变性能声明前重新运行 `pnpm bench:node`。
## 相关链接
- [英文原文](/COMPARISON)
- [中文首页](/zh/)
---
# 迁移
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/MIGRATION.md
---
title: "迁移"
description: "从 pino、winston 或 console.log 迁移到 LoggerJS。"
---
# 迁移说明
LoggerJS 仍是 pre-1.0,但当前代码库已经从初始骨架走向 v1 架构。本页前半部分说明如何从其他 logger 迁移;后半部分说明 LoggerJS 内部词汇变化。
## 从 pino 迁移
相同 levels、相同 numeric values、相同 NDJSON 直觉,映射大多是机械的。
```ts
// pino
import pino from "pino";
const logger = pino({ level: "info", base: { service: "checkout" } });
logger.info({ orderId: "ord_123" }, "order created");
const child = logger.child({ requestId: "req_1" });
// loggerjs
import { createLogger, stdoutTransport } from "@loggerjs/node";
const logger = createLogger({
level: "info",
tags: { service: "checkout" },
transports: [stdoutTransport()],
});
logger.info("order created", { orderId: "ord_123" }); // message first, data second
const child = logger.child({ bindings: { requestId: "req_1" } });
```
关键差异:
- **参数顺序翻转**:pino 是 `(mergeObject, message)`,LoggerJS 是 `(message, data)`。Errors 在两者中都放前面:`logger.error(err, "msg")`。
- pino `base` fields 拆为 `tags`(稳定、低基数)和 `bindings`(合并到 `context` 的上下文字段)。
- pino `serializers` 变成 processors(`normalizeErrorProcessor`、`redactProcessor`、自定义 `enrichProcessor`),在序列化前作用于结构化数据。
- pino redaction 映射到 `redactProcessor({ paths, censor, remove })`;`replacement` 是 LoggerJS 原生命名,等价于 `censor`;热日志器优先使用精确 key/path matching。
- pino `transport`/`destination` 变成 LoggerJS transport:`stdoutTransport()`、`fileTransport()`、`nodeHttpTransport()`。
- pino-pretty 的角色由 `prettyStdoutTransport()` / `prettyStderrTransport()`(terminal)或 `prettyConsoleTransport()`(browser DevTools)承担。Core `consoleTransport()` 仍是基础本地 console sink。
- 需要 Pino-shaped NDJSON 时,使用 `@loggerjs/codecs` 的 `pinoCompatCodec()`。Root data merging 是 opt-in(`mergeData: true`),保留键冲突默认嵌套,而不是覆盖 `time`、`level`、`msg`、`pid`、`hostname` 或 `err`。
- 最快 LoggerJS lean envelope 使用 `fastEventJsonCodec({ includeId: false, includeSeq: false, includeLevelName: false })`。Record-aware custom transports 可以用 `createPreparedRecordEncoder(codec)` 包装它,复用稳定 logger/tag fragments。在 M1 Max 参考机器上,plain lean path 约 1.19x pino,prepared lean path 约 1.28x(paired A/B;相对 pino 排序依赖 CPU/V8,见 [基准](BENCHMARKS.md));在这个吞吐基础上,你还得到 middleware、integrations、multi-transport fan-out 和同构浏览器故事。
## 从 winston 迁移
```ts
// winston
import winston from "winston";
const logger = winston.createLogger({
level: "info",
format: winston.format.json(),
defaultMeta: { service: "checkout" },
transports: [new winston.transports.Console(), new winston.transports.File({ filename: "app.log" })],
});
// loggerjs
import { createLogger, fileTransport, stdoutTransport } from "@loggerjs/node";
const logger = createLogger({
level: "info",
tags: { service: "checkout" },
transports: [stdoutTransport(), fileTransport({ path: "app.log" })],
});
```
关键差异:
- winston `format` chains 拆成两个关注点:**processors/middleware**(数据塑形:redact、enrich、filter)和 **codecs**(序列化,由每个 transport 拥有)。`format.combine(timestamp, json)` 通常就是默认输出。
- `defaultMeta` -> `tags` 和/或 `bindings`。
- Per-transport `level` 直接映射到任何 transport 的 `minLevel`。
- Child loggers 替代 `winston.loggers` registries 做 per-module configuration;库作者优先使用 core 的 `getLogger()`。
- 当前快照中最快可比路径约为 winston 的 11x(见 [基准](BENCHMARKS.md))。
## 从 console.log 迁移
两种迁移方式可以组合使用。
**先捕获,逐步迁移**:不改调用点,把已有 console calls 转成结构化日志:
```ts
import { captureConsoleIntegration, createLogger, browserHttpTransport } from "@loggerjs/browser";
const logger = createLogger({
transports: [browserHttpTransport({ url: "/api/logs" })],
integrations: [captureConsoleIntegration({ levels: ["log", "warn", "error"] })],
});
```
**再在值得结构化的调用点替换**:
```ts
// before
console.log("order created", orderId);
console.error("payment failed", err);
// after
logger.info("order created", { orderId });
logger.error(err, "payment failed");
```
每一步获得的收益:levels 和 level gating、结构化数据替代插值字符串、数据离开进程前 redaction、batching/offline delivery,以及通过 error/process integrations 捕获 crash path。
---
## Processor 到 Middleware 词汇
`@loggerjs/processors` 包继续为兼容性保留。新文档把这一层称为同步 middleware,因为行为范围比 event processors 更广:redaction、enrichment、sampling、tags、type、dedupe 和 trace attachment 都在 transport delivery 前运行。
现有代码可以继续使用:
```ts
import { redactProcessor } from "@loggerjs/processors";
```
新的 core middleware 可以使用:
```ts
import { createMiddleware } from "@loggerjs/core/middleware";
```
## LogEvent 和 LogRecord
`LogEvent` 仍是面向 transport 的兼容形状。Core record helpers 现在内部使用 `LogRecord`,让热路径在投影成 transport events 前可以保留 lazy messages、raw errors、bound context 和稳定 record shape。
Transport authors 应继续通过当前 public `Transport` interface 接收 `LogEvent`。Codec authors 应使用导出的 codec input helpers,而不是进入 logger internals。
## Context
显式 context 使用 child loggers:
```ts
const requestLogger = logger.child({ requestId: "req_123" });
```
Request scopes 使用 ambient context:
```ts
import { withContext } from "@loggerjs/core";
import { installAsyncLocalStorageContext } from "@loggerjs/node";
installAsyncLocalStorageContext();
await withContext({ requestId: "req_123" }, async () => {
logger.info("request started");
});
```
## Browser Integrations
浏览器采集仍然是 opt-in。已有手动 logging 代码不会自动捕获 console、errors、fetch 或 XHR,除非配置了对应 integration。
优先使用:
```ts
captureConsoleIntegration({ levels: ["warn", "error"] });
captureBrowserErrorsIntegration();
captureFetchIntegration();
pageLifecycleIntegration();
```
## Transports 和 Codecs
序列化属于 transports。把 JSON/stringification 工作从 processors 移到 transport codec:
```ts
browserHttpTransport({ url: "/api/logs", codec: safeJsonCodec() });
```
Batch-based transports 现在共享 queue、retry、byte-limit、concurrency 和 circuit-breaker options。
## Package Imports
Root package imports 仍然可用:
```ts
import { createLogger } from "@loggerjs/core";
```
稳定 subpaths 可用于更窄 imports:
```ts
import { createMiddleware } from "@loggerjs/core/middleware";
import { browserHttpTransport } from "@loggerjs/browser/transport-http";
import { stdoutTransport } from "@loggerjs/node/transport-stdout";
```
当前 build 发布 ESM 和 CJS 入口。Type declarations 按 NodeNext-style package resolution 检查。
## 相关链接
- [英文原文](/MIGRATION)
- [中文首页](/zh/)
---
# API 稳定性
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/API-STABILITY.md
---
title: "API 稳定性"
description: "v1 稳定 API 子集和 pre-1.0 兼容策略。"
---
# API 稳定性
LoggerJS 仍处于 pre-1.0。签入仓库的 `api-reports/` 文件描述了每一个已导出的 TypeScript 声明,但这并不表示每个导出符号都已经冻结为 v1 API。
本页是面向人的稳定性契约。机器可读分类位于 [`docs/api-stability.policy.json`](api-stability.policy.json),当某个 package export 未被纳入策略时,`pnpm verify:api-stability` 会失败。
## 当前策略
在 v1 前,项目选择收窄兼容承诺,而不是冻结整个仓库。稳定集合被刻意限制在 core logger model、core pipeline contracts、主要 browser 和 Node delivery paths、pretty output、processors 和 codecs。
其他部分仍然可能是公开的、经过测试的、可用的,但并不全部属于 v1 兼容承诺。尤其是 vendor、observability 和 database packages,在获得更多真实使用和失败模式验证前,都保持 experimental。
## 状态级别
| 状态 | 含义 |
| --- | --- |
| Stable v1 Candidate | 计划带入 v1,不做移除、重命名或签名破坏;安全、数据丢失或 wire-protocol 正确性修复除外。允许 additive changes。 |
| Compatible Public Surface | 公开且有测试,但还没稳定到 v1 candidate。pre-v1 minor releases 可能通过 release notes 调整 option names、captured fields 或 runtime edge behavior。 |
| Experimental Before v1 | 公开 packages 或 subpaths 在 v1 前可能变化。当前行为适合时可以使用,但不要把它们视为冻结的兼容契约。 |
内部源码路径、`dist` 文件路径、生成 bundle 布局、private class fields,以及只从测试推断出的行为,在任何状态下都不是 public API。
## Stable v1 Candidate
稳定 exports 在 `api-stability.policy.json` 中跟踪。当前稳定 packages 和入口家族如下:
| Package | 稳定 surface |
| --- | --- |
| `@loggerjs/core` | Root package,以及 middleware、codecs、events、context、trace propagation、payload transforms 和 core transports 的文档化 core subpaths。 |
| `@loggerjs/browser` | 用于 HTTP delivery、IndexedDB/offline-first storage、payload transforms,以及主要 console/error/fetch/XHR/context/performance/page-lifecycle integrations 的文档化稳定 subpaths。 |
| `@loggerjs/node` | 用于 stdout/stderr/file/rotating-file/HTTP/syslog/worker transports、payload transforms、process capture、outgoing HTTP capture、diagnostics 和 AsyncLocalStorage context 的文档化稳定 subpaths。 |
| `@loggerjs/pretty` | Root package、formatter、console transport 和 stream transports。 |
| `@loggerjs/processors` | Root package processor 和 middleware catalog。 |
| `@loggerjs/codecs` | Root package codec catalog。 |
稳定语义包括:
- 用于应用和 library-safe logging 的 `createLogger(options)`、`getLogger(category)` 和 `configure(...)`。
- Logger instance methods:`trace`、`debug`、`info`、`warn`、`error`、`fatal`、`log`、`capture`、`event`、`child`、`withTags`、`withType`、`setLevel`、`getLevel`、`isEnabled`、`isLevelEnabled`、`addTransport`、`addProcessor`、`addIntegration`、`ready`、`flush`、`flushSync` 和 `close`。
- Level 名称和数值:`trace=10`、`debug=20`、`info=30`、`warn=40`、`error=50`、`fatal=60` 和 `silent`。
- `Middleware`、`Processor`、`Transport`、`Integration`、`Codec` 的 pipeline interfaces,包括 `TransportContext.toEvent(record)` 的 memoized projection。
- 禁用级别在 record 分配和 lazy message 求值前返回。
- Middleware、processors、codecs、integrations 和 transports 的错误与应用代码隔离。
- 序列化仍然归 transport 所有;middleware 和 processors 保持结构化值。
## Compatible Public Surface
Compatible exports 仍然会保持文档化和测试,但还没有稳定到 v1 candidate。当前 compatible areas 包括:
- Browser 和 Node root packages(`@loggerjs/browser`、`@loggerjs/node`)是 compatible convenience aggregators,因为它们同时 re-export stable 和 compatible components。需要 v1 candidate 兼容边界时,优先使用上面的 stable subpaths。
- Browser secondary transports 和 collectors:BroadcastChannel、service worker、WebSocket、ZIP export、framework errors、framework routers、generic router capture、ReportingObserver、runtime host、service worker messages、user actions 和 WebSocket capture。
- Node framework 和 data integrations:Express、Fastify、Koa、Nest、Hapi、Prisma、Redis、generic queues、BullMQ、serverless lifecycle、database method wrapping 和 CLI capture。
pre-v1 期间这些 public import paths 应保持可用,但具体 captured fields、hook coverage 和 edge behavior 仍可能被调整。如果真实使用表明当前 API 太宽,这些区域也是 v1 前收窄命名或降低承诺的合适位置。
## Experimental Before v1
这些 packages 是公开的,因为它们对集成测试和早期采用者有用,但不是 v1 兼容承诺:
| Package family | Experimental exports |
| --- | --- |
| Observability adapters | `@loggerjs/otel/*`, `@loggerjs/sentry/*` |
| Vendor wire transports | `@loggerjs/datadog/*`, `@loggerjs/elastic/*`, `@loggerjs/loki/*`, `@loggerjs/cloudwatch/*` |
| Database transports | `@loggerjs/database/*` |
Experimental 不等于未测试。它表示 v1 前的 minor releases 可能根据 design partners 或 live endpoints 暴露出的更好形态,调整 option names、payload mapping、retry expectations、batching guidance 或 subpath layout。
Raw vendor transports 本身不是 durable。生产投递应使用 `batchTransport()` 和 `retryTransport()` 包装,或投递到由 collector endpoint 负责 queueing、retry、authentication 和 backoff 的服务。
## 变更策略
对 Stable v1 Candidate APIs:
- v1 前不故意移除、重命名或破坏签名,除非有 deprecation note 和 migration path。
- 允许 additive changes:新 options、fields、overloads、processors、transports、integrations 和 subpaths。
- 影响 delivery、privacy 或 performance 的 defaults 需要文档和 release notes。
- 安全修复、数据丢失修复、vendor wire-protocol 正确性修复可能改变 edge-case behavior。Release notes 必须说明这些变化。
对 Compatible 和 Experimental APIs:
- Public exports 仍保持 typechecked、tested、API-reported 和 documented。
- pre-v1 minor releases 可以调整 names、options、field shape 或具体 behavior。
- Breaking changes 仍应带 release notes 和 migration guidance,因为 public 不等于 disposable。
## 新增 Public API
新的 package exports 必须:
1. 在最接近实际 runtime 的层级新增或更新测试。
2. 更新 import boundaries 和 caveats 的文档与示例。
3. 把 export 加入 `docs/api-stability.policy.json`。
4. 运行 `pnpm verify:api-stability` 和 `pnpm api:check`。
当现有稳定 API 能解决问题时,优先使用示例和组合,而不是新增 exports。
## 如何评估未来升级
1. 阅读 package changelog 和 release notes。
2. 查看本页和 `api-stability.policy.json`,确认你依赖的 export 状态。
3. 如果你是贡献者,在本仓库运行 `pnpm check`;如果你是消费者,运行自己的应用测试套件。
4. 热路径变更用 `pnpm bench:node` 或 `pnpm bench:browser` 复现相关 benchmark。
5. 远程投递要测试你的真实 collector/vendor endpoint,并监控 `transport.dropped.*`、`transport.retry.*` 和 queue-depth metrics。
## 相关链接
- [英文原文](/API-STABILITY)
- [中文首页](/zh/)
---
# 架构
Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/ARCHITECTURE.md
---
title: "架构"
description: "设计规则、管线内部和记录过的技术决策。"
---
# LoggerJS 架构
> 状态:当前面向 v1 的代码库实现架构。
> 来源输入:`DESIGN.md`、`log.md` 和当前 monorepo skeleton。
LoggerJS 是面向浏览器、Node、Bun、Deno 和 edge runtimes 的同构结构化 logger。产品架构围绕三个面向用户的概念构建:
- **Integration**:opt-in 自动采集,例如 browser console capture、global script errors、HTTP errors、page lifecycle flush、Node process errors 和 runtime diagnostics。
- **Middleware**:同步 record transforms 和 filters,例如 redaction、sampling、tag/type enrichment、request correlation、dedupe 和 route-specific policies。
- **Transport**:目的地边界,例如 console、stdout、file、HTTP batch、OTLP、Sentry、DB、worker-hosted delivery 或任意用户自定义 sink。
还有一个必须保持一等地位的技术边界:**Codec**。Codec 属于 transport,并拥有 serialization/deserialization。Middleware 不得序列化 records。Console transport 应保留原始值。HTTP/file/OTLP transports 选择各自需要的 codec。
## 当前仓库基线
当前仓库已经具备主要 v1 building blocks:
```text
packages/core Logger, LogRecord helpers, LogEvent projection, context, typed events, codecs, console/memory/batch transports
packages/browser Browser HTTP transport, offline queue, beacon/page lifecycle flush, console/error/fetch/XHR integrations
packages/node stdout/stderr/file/http/worker transports, AsyncLocalStorage context, process and diagnostics-channel integrations
packages/processors redact/sample/tags/type/dedupe/trace processors
packages/codecs fixed-shape JSON, built-in msgpackr, projector codec
packages/otel OTLP JSON mapping, HTTP transport, active span trace processor
packages/sentry Sentry structured logs, breadcrumbs, exception/message transport
examples/* browser and node basic demos
```
剩余架构工作主要是打磨和 package topology:
- `Processor` 仍作为兼容词汇支持,而 `Middleware` 是公开心智模型。
- `LogEvent` 仍作为面向 transport 的兼容 envelope;热路径构造 `LogRecord`,只在需要时投影。
- 粗粒度 browser/node packages 可以保留为 presets,但稳定 v1 packages 应把平台 transports 和 integrations 拆成更小的 installable units。
- 当前双 ESM/CJS 输出保留用于兼容。Declaration output 兼容 NodeNext,并验证 public subpath exports。
- Batch transports 已覆盖 bounded queues、byte limits、retry、drop counters、circuit breaking、pagehide/beacon behavior 和 runtime flush semantics。
## 不可谈判的设计规则
1. **Core 平台中立。** `@loggerjs/core` 不得 import browser、Node、Bun、Deno、worker、filesystem、fetch 或 diagnostics APIs。
2. **禁用日志几乎免费。** 禁用级别调用必须只做一次数字 level 比较,并在 record allocation、message stringification、context merge 或 integration work 前返回。
3. **序列化只发生在 transport 边界。** 管线保留原始引用。`resolveMessage(record)` 是唯一允许 middleware 触发的 lazy evaluation。
4. **Middleware 同步。** 热路径中没有 promises、没有 Koa-style `next`、没有 async lookup。
5. **Integrations 使用与手动日志相同的管线。** 自动 records 只是在 `source` 上不同;它们仍经过 middleware、routing、batching、codec 和 transport policy。
6. **Integrations 显式且可逆。** 任何 monkey patch 都必须 opt-in、idempotent、防重入,并能完全 teardown。
7. **Logger 错误永远不逃逸到应用代码。** 内部失败通过 rate-limited meta logger 计数和报告。
8. **v1 不使用 object pool。** 短生命周期 records 应保持 young-generation GC objects,除非 benchmark 证明需要其他方案。
## 端到端管线
```text
manual API / integration capture
|
v
level gate
|
v
create LogRecord
|
v
global middleware
|
v
transport router / fan-out
|
+--> per-transport middleware
| |
| v
| transport buffer
| |
| v
| codec.encode(batch)
| |
| v
| sink: console / stdout / file / HTTP / OTLP / worker / custom
|
+--> ...
```
在选定 transport 准备投递之前,record 绝不应被 stringify。这让 console 能保留可交互对象,HTTP 能选择 JSON 或 binary,file 能选择 NDJSON,OTLP 能选择自己的 wire mapping,而且不惩罚其他目的地。
## Core Record Model
目标内部 record 是 `LogRecord`,不是当前 `LogEvent` envelope。它为稳定 hidden class 和 transport-owned projection 优化:
```ts
export interface LogRecord {
time: number;
level: number;
category: readonly string[];
msg: string | null;
lazy: (() => string) | null;
props: Record | null;
err: unknown;
ctx: BoundContext | null;
source: string;
stack: string | null;
seq: number;
}
```
实现规则:
- 通过单一 `createRecord()` 路径构造 records。
- 以相同顺序分配每个字段,包括 `null` 字段。
- 不 `delete` 字段,不向 record 附加 ad hoc properties。
- Extra data 放在 `props` 或 immutable `ctx` 中。
- `time` 是 `Date.now()`;同一 timestamp 内用 `seq` 排序。
- `err` 与 `props` 分离,因为 error encoding、stack truncation、cause handling 和 dedupe 是专门逻辑。
当前 `LogEvent` 形状可以暂时保留为 codec projection 或兼容类型,但 v1 rewrite 开始后不应驱动热路径。
## Logger API
LoggerJS 支持两种 acquisition models:
```ts
const log = createLogger({
category: "app",
level: "info",
transports: [consoleTransport()]
});
```
```ts
const log = getLogger(["library", "parser"]);
await configure({
middleware: [redact({ paths: ["password", "*.token"] })],
transports: {
console: consoleTransport(),
http: httpTransport({ url: "/v1/logs", codec: jsonCodec() })
},
loggers: [
{ category: ["app"], level: "debug", transports: ["console", "http"] },
{ category: ["library"], level: "warn", transports: ["http"] }
],
integrations: [consoleIntegration(), globalErrorsIntegration()]
});
```
必需调用形态:
```ts
log.info("user logged in", { userId: 42 });
log.error(err, "save failed", { orderId });
log.debug(() => expensiveDebugMessage());
log.event(CheckoutCompleted, { orderId, amountCents });
log.child({ requestId }).warn("retrying");
await log.flush();
```
Overload 规则保持很小:
- 第一个参数是 `string`:message
- 第一个参数是 `function`:lazy message
- 其他情况:error slot,后面可选 message 和 props
Core 不包含 printf-style formatting。结构化字段是一等公民;formatting 属于显示层。
## Registry 和 Configuration
`getLogger(category)` 为库作者存在。配置前返回 void logger;`configure()` 后通过配置好的管线路由。
配置要求:
- 按 category 做 prefix matching,例如 `["app"]` 应用于 `["app", "checkout"]`
- named transports 和 named middleware
- 显式 integration lifecycle management
- 可选 early ring buffer,用于 pre-config logs
- `configure({ reset: true })` 替换旧 transports 并调用 async disposal hooks
- immutable runtime snapshots,让热路径读取不遍历 mutable config structures
这个 registry 是战略功能:第三方库可以记录日志,而不耦合到任何 backend,也不强迫应用配置。
## Context
有两类 context:
- **显式 context**:`logger.child(bindings)`。Child bindings 在 child 创建时 flatten 并 freeze。
- **隐式 context**:`withContext(bindings, fn)`。Node/Bun/Deno 使用 AsyncLocalStorage 或等价 conditional exports。浏览器初期降级为 synchronous-scope context,直到 TC39 AsyncContext 可用。
Codec-level context optimization 替代 pino-style global chindings:
```ts
interface EncodeContext {
levelName(level: number): string;
ctxCache: WeakMap