# 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; schemaCache: WeakMap; } ``` 每个 codec 可以缓存 immutable bound contexts 的 encoded fragments。这样保留性能收益,同时不让 JSON serialization 成为全局 logger concern。 ## Middleware 目标接口: ```ts export interface Middleware { readonly name: string; process(record: LogRecord): LogRecord | null; } ``` 执行模型: - global middleware 在 fan-out 前运行一次,可以原地修改单一 record - per-transport middleware 在 fan-out 后运行,必须把 record 视为共享值 - per-transport changes 使用 `cloneRecord(record, patch)` 来保留形状并避免跨 transport 泄漏 - 返回 `null` 表示丢弃 record - middleware exceptions 会被捕获和计数,不会阻止剩余管线,除非 middleware 明确丢弃 内置能力应覆盖: - `redact`:安全 path/key redaction,在 matched branches 上 copy-on-write - `sample`:按 level/category/key sampling,error 和 fatal 默认完整保留 - `rateLimit`:按 category/level/source 的 token bucket - `dedupe`:fingerprinted burst collapse - `fingersCrossed`:由 error trigger 释放的低级别 ring buffer - `enrich`:同步 props/context enrichment - `tags` 和 `type`:当前 processor 行为的 thin compatibility helpers - `traceContext`:OTel 或用户提供的 trace/span injection Middleware 不得调用 `JSON.stringify`、`String(record.props)` 或递归标准化整个 records。如果需要 message,必须有意调用 `resolveMessage(record)`。 ## Transports 目标接口: ```ts export interface Transport { readonly name: string; write(record: LogRecord): void; flush(): Promise; flushSync?(): void; dispose(): Promise; filter?(record: LogRecord): boolean; middleware?: Middleware[]; } ``` Transport 职责: - final routing filters - queue 和 backpressure policy - batching - retry 和 circuit breaking - codec selection 和 serialization - destination-specific delivery - drop/error counters - flush 和 disposal semantics ### Batching Base 共享 batching 实现应支持: - `maxRecords` - `maxBytes` - `maxWaitMs` - `concurrency` - 带 exponential backoff 和 full jitter 的 retry - `drop-old` 和 `drop-new` - drop counters 和 hooks - 带 half-open recovery 的 circuit breaker - 队列为空时不保留 idle timer - ship 时做 encoded-size accounting 当前 `batchTransport()` 可以作为 bootstrap utility,但不是完整 v1 reliability layer。 ### Console Transport Console transport 在 pretty mode 下不应序列化。它应把原始 `msg`、`props` 和 `err` 引用传给原始 console methods,让浏览器 devtools 保持对象检查能力。 它必须使用 unpatched console registry,才能和 console capture 共存而不形成 feedback loops。 ### HTTP Transport HTTP transport 是带平台实现的共享抽象: - Browser:`fetch`、`keepalive`、`pagehide`/`visibilitychange` 上的 `sendBeacon`、可选 IndexedDB offline queue,以及围绕 64 KiB beacon budget 的严格 payload limits。 - Node:global `fetch`/undici、retry/circuit breaker,不宣称 sync crash flush。 - Edge:`waitUntil` hook,用于 response-lifetime-safe delivery。 隐私默认值: - 不采集 request/response body - 不采集 headers,除非 allowlisted - 不启用 offline disk persistence,除非显式配置 ### File 和 Stdout Transports Node stdout/stderr/file transports 默认应输出 NDJSON。File transport 需要真实 `flushSync()` 路径,使用 `fs.writeSync` 或等价 crash-safe primitive。仅 async stream writes 不足以覆盖 fatal process events。 ### Worker Transport Node worker transport 应把 IO 和 retry state 移出主线程。首选路径: ```text main thread batch -> codec.encode(batch) -> Uint8Array -> postMessage(buffer, [buffer]) ``` worker 失败时,transport 应降级到 inline mode 并发出 meta warning。跨 worker boundary 不提供 `flushSync`。 ### OTLP 和 Sentry OTLP/HTTP JSON 是第一方 transport,因为 LoggerJS 应接入现有 observability backends,而不是发明新的 logging backend protocol。 Sentry 支持应作为 adapter package。LoggerJS 把 records 映射成 Sentry structured logs,并可选择把 error records 捕获为 Sentry events。 ## Codecs 目标接口: ```ts export interface Codec { readonly name: string; readonly contentType: string; encode(batch: readonly LogRecord[], ctx: EncodeContext): Out; decode?(data: Out): unknown[]; } ``` 必需 codecs: - `jsonCodec`:默认 NDJSON/log JSON codec,固定 field ordering,对普通 props 使用 native `JSON.stringify`,只在失败 branches 上安全 fallback。 - `structuredCodec`:保留丰富值的 codec,可对 Error、cause chains、AggregateError、circular/shared references、BigInt、Date、RegExp、URL、Map、Set、TypedArray、ArrayBuffer、`undefined`、`NaN`、infinities 和 `-0` 做对称 decode。 - `msgpackCodec`:二进制 batch codec,可以是 benchmark-proven custom subset,也可以是 `msgpackr` 的小 adapter。 - `projectorCodec`:自定义 wire schemas 的 utility adapter。 Structured codec 应使用 flat value-pool format,不使用 `eval`、`new Function` 或 recursive revivers。Decode 应是 `JSON.parse` 加确定性的 pointer restoration pass,使它 CSP-friendly,并适合 browser replay tools。 ## Integrations 目标接口: ```ts export interface Integration { readonly name: string; setup(api: IntegrationAPI): Teardown; } export interface IntegrationAPI { capture(input: CaptureInput): void; getLogger(category: string | readonly string[]): Logger; unpatched: UnpatchedRegistry; guard any>(fn: T): T; } ``` Loop prevention 有三层: 1. patch 前注册原始 console/fetch/XHR functions。 2. Guard 同步 logger execution,让 reentrant capture 被丢弃并计数。 3. 保留 `record.source`,让 transports 过滤 self-generated records。 必需 browser integrations: - console capture for `log`, `info`, `warn`, `error`, `debug`, and `trace` - global script/resource errors - `unhandledrejection` - optional `securitypolicyviolation` - fetch 和 XHR HTTP error collection - page lifecycle flush hooks - optional offline replay hooks 必需 Node integrations: - `uncaughtException` - `unhandledRejection` - `warning` - `beforeExit`/`exit` flush handling - diagnostics_channel subscriptions for undici 和 Node HTTP where available Node crash behavior 必须诚实。启用 `exitOnUncaught` 时,fatal capture 应 flush sync-capable transports,对其余 transports 尝试有界 async flush,然后保留 process exit semantics。Integration 不得静默把 fatal crashes 变成 zombie processes。 ## Routing Routing 使用 category、level、source、tags/type 和显式 transport filters。 配置应支持: - category prefix rules - per-transport minimum levels - source exclusions,例如从 console transport 排除 `integration:console` - per-transport middleware - 可在 presets 中复用的 named routes Routing 必须解析成 immutable runtime snapshots,避免每次 log call 做昂贵 dynamic config lookup。 ## 性能预算 初始内部预算: | Path | Target | | --- | --- | | Disabled level call | 一次数值比较,零分配 | | Enabled record to queue, 3 middleware, no stack | 主流桌面 CPU 上 <= 1 microsecond per record | | JSON/NDJSON codec | 对普通对象达到 million-records-per-second 级别 | | Node NDJSON full path | v1 前在等价输出下至少达到 pino 的 80% | | Core size | <= 4 KB min+gzip(理想目标,尚未达到,见下方说明) | | Record allocation | 一个 record object;除非 middleware 显式 clone,否则不复制 data | 截至 2026-06,上方 `<= 4 KB` core-size 行是尚未达到的 aspiration,不是当前状态。当前测量值(并由 `pnpm size:check` 强制):完整 `@loggerjs/core` barrel 约 18 KB gzip;最小 tree-shaken import(`createLogger` + 一个 `consoleTransport`)约 6 KB min+gzip。在预算真正达到前,公开 size 表述应锚定这些测量数字。 Benchmark 必须覆盖 Node 和真实浏览器,而不只是 synthetic Node loops。套件应比较 pino、winston、LogTape、native console、native `JSON.stringify`、当前 LoggerJS 和目标 LoggerJS paths。 ### 决策:保留 record pipeline,通过 codec-owned preparation 优化 截至 2026-06,在参考机器(Apple M1 Max,Node v22.21.1)上,用 drift-canceling paired A/B harness 测得 lean Node NDJSON path 约为 pino 的 1.19x,codec-owned prepared lean path 约为 1.28x,即在等价输出下 **快于 pino**。Full-envelope path 约为 pino 的 0.9x,同时在 pino 字段外额外输出 `id`、`seq` 和 `levelName`(见 `docs/BENCHMARKS.md`)。这个排序 **依赖 CPU/Node-V8**:pino 和 loggerjs 都使用手调 JSON hot paths,当前文档把差异视为经验 benchmark 结果,而不归因于低层机制。不同芯片或 Node/V8 构建上 pino 可能领先。重点是 loggerjs 在 **不把序列化移入 logger** 的前提下达到 pino 同级别。 达成这一点经历了 2026-06 的 profiling,也修正了早前“差距是结构性的,不是未优化代码”的过度表述。三个不改变架构的改动,把本机 lean ratio 从约 1.30x pino 推到约 0.84x:第一,未配置 ambient context 时,`getContext` 不再每次执行 `addedProviders.map()` + spread + `mergeContext({})`;第二,`fastEventJsonCodec` 在 codec 创建时 bake `includeX` toggles,并用单个 template 输出 header;第三,codec-owned prepared record encoders 让 transports 复用 logger/category/tags fragments,而不让 logger 拥有 JSON serialization。 LoggerJS 仍然每条日志分配一个 `LogRecord`,让 middleware、processors、integrations 和多个 transports 观察同一个共享值;codec 仍拥有 never-throw safe-fallback contract,而且在已测硬件上已经追平或超过 pino。默认拒绝“当 logger 正好只有一个 sync transport 且没有 middleware 时绕过 record 的 fusion fast path”,理由更充分,因为它会: - 制造性能悬崖:新增第一个 middleware 会静默损失 30%+ 吞吐; - 把 serialization 移进 logger,破坏 codec-belongs-to-transport 边界; - 让每个语义变更都要维护两条 hot-path surface(2026-06 修复的 id-drift 和 source round-trip bugs 正是这种 dual-path 缺陷)。 剩余性能预算投向默认路径(batch enqueue、默认 codecs、prepared codec contracts)和 regression gating,而不是 fusion-only 峰值。只有真实生产用例证明单独语义 hot path 有价值时才重新讨论。 ## 可靠性 默认语义是 **best-effort at-most-once**。LoggerJS 不会为了保证日志投递而无限期阻塞应用进度。 每条损失路径都必须可观测: - queue overflow - batch too large - retry exhausted - circuit breaker open - beacon failed - offline queue quota exceeded - flush deadline exceeded - integration loop guard drop - middleware/transport exception 这些 counters 应通过 meta logger 和可选 stats APIs 暴露。 ## 隐私和安全 默认值: - redact 常见敏感 key:authorization、cookie、set-cookie、password、passwd、token、secret、apiKey、api_key 和 `*_key` - fetch/XHR integrations 不捕获 bodies - fetch/XHR integrations 不捕获 headers,除非 allowlisted - browser offline queue 默认关闭 - 默认 builds 中无 `eval` 或 generated code - core 无 runtime dependencies 任何把 logs 写入 durable browser storage 的功能都必须显式启用,因为它改变应用隐私姿态。 ## 测试策略 必需测试层: - core record construction、level gate、overloads、child context、middleware、router 和 transport errors 的单元测试 - safe JSON 和 structured round-trip behavior 的 codec property tests - 同时启用 console transport 和 console integration 的 loop prevention tests - pagehide/beacon flush、fetch/XHR capture、global errors 和 offline queue behavior 的浏览器 Playwright tests - uncaught exception flush 和 exit semantics 的 Node child-process tests - Node、Bun、Deno 和 workerd/miniflare runtime smoke tests - core 和 integration packages 的 size-limit checks - 带显式阈值的 benchmark regression checks 任何 milestone 都不能在缺少 examples、tests,以及至少一个与变更层相关的 benchmark 或 size measurement 的情况下完成。 ## Package 方向 v0 package layout 可以支撑开发,但 v1 public layout 应朝以下方向移动: ```text @loggerjs/core @loggerjs/transport-http @loggerjs/transport-otlp @loggerjs/transport-file @loggerjs/transport-worker @loggerjs/codec-structured @loggerjs/codec-msgpack @loggerjs/integration-console @loggerjs/integration-global-errors @loggerjs/integration-fetch @loggerjs/integration-node @loggerjs/otel @loggerjs/sentry @loggerjs/pretty @loggerjs/browser preset/meta package @loggerjs/node preset/meta package ``` 允许 preset packages,但平台 API 的 ownership 应位于小包中,让用户只安装需要的 collection 和 transport surface。 ## v1 完成标准 LoggerJS 达到 v1 readiness 的条件: - core public API 由 API report 锁定 - disabled hot path 和 enabled queue path 在 Node 与浏览器上达到预算 - codec JSON、structured 和 msgpack paths 有 benchmark data - browser 和 Node integrations 有 loop 和 teardown tests - OTLP collector demo 能端到端工作 - crash flush behavior 由 child process 测试覆盖 - privacy defaults 被文档化并测试 - examples 覆盖 browser、Node service、edge worker 和 OTLP collector - migration guide 覆盖 console.log、pino、winston 和 LogTape-style library logging ## 相关链接 - [英文原文](/ARCHITECTURE) - [中文首页](/zh/) --- # 贡献 Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/CONTRIBUTING.md --- title: "贡献" description: "仓库工作流、CI gate 和工程约定。" --- # 贡献 ## 设置 ```bash pnpm install # repository development uses Node >=22.13, pnpm >= 11.5.3 pnpm check # 完整门禁,push 前运行 ``` `pnpm check` 与 CI 在每个 pull request 上运行的门禁一致:format check(oxfmt)、lint(oxlint)、typecheck、tests(vitest)、builds(rolldown + tsc)、size budgets、export map verification、public type surface check、API report check 和 npm pack validation。CI 还会额外运行 `pnpm bench:gate`。 ## Node 版本策略 - **仓库开发**:使用 Node `>=22.13.0`。root `package.json` 的 `engines` 字段和本地工具链有意设定在这个下限。 - **完整 CI 门禁**:在 Node 22 和 24 上运行 `pnpm check`,release 使用 Node 24 构建。 - **发布包 runtime 兼容性**:packed packages 会作为 consumer 在 Node 20.19.0、22 和 24 上做 smoke test。Node 20.19.0 是 Node 消费者 runtime 兼容下限;它不降低仓库开发工具链要求。 ## 仓库布局 ```text packages/core platform-neutral kernel: logger, record/event model, registry, context, middleware, integration API, console/memory/test/batch transports, json/safe-json/ndjson codecs packages/browser browser transports + integrations packages/node node transports + integrations + AsyncLocalStorage context packages/processors middleware/processor toolbox packages/codecs fast-event-json, built-in msgpackr, projector packages/otel|sentry|datadog|elastic|loki|cloudwatch|database destination adapters examples/ runnable examples per platform scripts/ build/verify/bench/release tooling docs/ this documentation api-reports/ checked-in public API surface per package ``` Turbo 用缓存编排 `build`/`test`/`typecheck`;可以用 `pnpm exec turbo run test --filter=...@loggerjs/core` 按包和依赖它的包缩小范围。 ## 会让 CI 失败的规则 **Commits** 遵循 Conventional Commits,并由 commitlint 强制。允许 scopes:`browser`、`build`、`codecs`、`core`、`deps`、`docs`、`examples`、`node`、`otel`、`processors`、`release`、`repo`。 **API reports**:任何 public surface 变更(包括 exported symbols 上的 JSDoc)都需要重新生成 reports:`pnpm build && pnpm api:report`,并提交 diff。`pnpm api:check` 会在漂移时失败。 **Size budgets**(`scripts/check-size-budgets.mjs`):每个包都有 build 后检查的 raw + gzip 上限。只有在同一个或相邻 commit 中随理由一起提交时,才提高预算。 **Component docs**(`scripts/verify-component-docs.mjs`):每个 public `transport-*`、`*-transport` 或 `integration-*` subpath 都必须出现在匹配的 transport/integration import-boundary docs 中。新组件还需要在同一变更中补稳定性和可靠性说明。 **Benchmark gate**(`pnpm bench:gate`):热路径场景以 paired A/B ratios 与匹配的 pino baseline 比较。限制故意宽松,用来抓结构性回归(每条日志意外分配、fast path 丢失),不是抓普通噪声。如果你的变更合理改变了 ratio,在 `scripts/check-bench-regression.mjs` 中带理由更新限制。 **Changesets**:发布包的用户可见变更需要 changeset(`pnpm changeset`);纯仓库工具改动不需要。 ## 工程约定 - **Core 保持平台中立**:无 DOM types、无 Node built-ins,通过 `globalThis` 做 feature-detect。Public type surface 必须能在没有 `lib.dom` 的环境下编译。 - **v1 前优先稳定而不是扩 surface**:先加固现有 transports/integrations。新增内置组件需要生产用例、合适的 runtime package placement、测试、稳定性文档、import-boundary docs 和 size-budget evidence。 - **管线永远不向应用抛错。** Middleware、processors、codecs 和 transports 都错误隔离;失败通过 `onInternalError` 和 meta counters 上报。新代码要保持这个性质。 - **Codecs 不得丢日志**:包住风险 encode,并 fallback 到 `safeJsonStringify`;在 meta 中计数 fallback。 - **共享对象冻结,替换而非修改**(`record.tags`、`record.ctx`)。 - **热路径变更需要数字。** 前后运行 `pnpm bench:node`,把相关行写进 commit message;当快照有实质变化时更新 `docs/BENCHMARKS.md`。Benchmark warmup 必须与 iterations 成比例,详见 BENCHMARKS.md 中关于曾经做错 warmup 的说明。 - **性能有文档化边界**:提出绕过 record 的 fast path 前,先阅读 [架构](ARCHITECTURE.md) 中 record-pipeline 决策。 ## 测试 各包使用 Vitest,测试在 `test/*.test.ts`。仓库风格: - 用 hostile inputs 固定行为(circular refs、BigInt、frozen objects、throwing callbacks)。多数历史回归正是这样被抓到的。 - 用 core 的 `testTransport()` 做 transport-side assertions;它提供 snapshots、stats 和 `waitForCount`。 - 新 transports/integrations 要带 teardown tests:patch、capture、restore,再断言没有 double-capture。 额外 CI gates 覆盖 runtime 和质量面: - `pnpm test:e2e:browser` 在 Chromium、Firefox 和 WebKit 中运行 browser E2E suite。 - `pnpm compat:runtimes -- --runtime=bun|deno|workers` 在 Bun、Deno 和 workerd/Miniflare runtime 中 smoke-test packed packages。 - `pnpm test:quality` 运行 coverage thresholds、mutation testing 和 concurrent soak runner。 - `pnpm test:live:local` 启动 Docker-backed Elasticsearch 和 Loki 实例,通过 transports 写入真实 log events,并查询服务确认。 - `pnpm test:live:external` 写入并查询 Datadog Logs 和 CloudWatch Logs。它需要 `DATADOG_API_KEY`、`DATADOG_APP_KEY`、`AWS_REGION`、`AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY` 和 `CLOUDWATCH_LOG_GROUP`;使用 `pnpm test:live:config` 可以在不打印 secret values 的情况下审计当前变量。 ## 发布 见 [发布](RELEASE.md)。简版:changesets 在 `main` 上累计;release workflow 负责 version、build、运行完整门禁,并带 provenance 发布。 ## 相关链接 - [英文原文](/CONTRIBUTING) - [中文首页](/zh/) --- # 发布 Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/RELEASE.md --- title: "发布" description: "版本和发布流程。" --- # 发布 LoggerJS 使用 Changesets 做版本管理,并通过 GitHub Actions release workflow 发布到 npm。 ## 本地验证 切 release 前运行完整 release dry-run: ```bash pnpm release:dry-run ``` 它会运行常规质量门禁、验证 public exports 和 API reports、用 `npm pack --dry-run --json` 检查每个包、把 publish 风格的 tarballs 安装进临时 consumer smoke project、打印 Changesets status,并运行 `pnpm publish -r --dry-run --access public --no-git-checks --json`。 不发布的 canary 验证: ```bash pnpm release:canary:dry-run ``` 真正的 `canary` dist-tag 只应从 versioned prerelease branch 或 workflow 使用: ```bash pnpm check changeset publish --tag canary ``` ## 新组件发布准备 发布新增 public transport 或 integration subpath 前,确认变更包含: - `docs/TRANSPORTS.md` 或 `docs/INTEGRATIONS.md` 中的稳定性分类; - `pnpm verify:component-docs` 检查的 import-boundary coverage; - 与组件匹配的 runtime 验证:浏览器 lifecycle/storage 行为用 browser E2E,edge/runtime 声明用 runtime smoke,本地服务用 Docker-backed live tests,hosted vendors 用 external-provider smoke; - `pnpm size:check` 的 size-budget evidence,任何预算增加都要在变更中说明理由; - 生产文档明确 delivery、retry、privacy 和 credential placement caveats。 ## NPM 发布 Release workflow 是 `.github/workflows/release.yml`。每个可发布的 `@loggerjs/*` 包都应在 npmjs.com 上配置 Trusted Publisher: - Organization or user: `jskits` - Repository: `loggerjs` - Workflow filename: `release.yml` - Allowed action: `npm publish` workflow 使用 npm Trusted Publisher/OIDC 发布到 npm;不读取 `NPM_AUTH_TOKEN`、`NPM_TOKEN` 或 `NODE_AUTH_TOKEN`。 - `permissions.id-token: write` 允许 GitHub Actions 生成 OIDC token,npm 在 `npm publish` 期间交换它。 - `actions/setup-node` 为 publish 命令设置 npm registry,不配置长生命周期 token。 - release job 运行在 GitHub-hosted Ubuntu runner 上,使用 Node 24 并升级到 npm 11,确保 Trusted Publisher 支持是新的。 - 发布前,release job 运行 `pnpm release:publish:preflight`,为每个未发布包交换 GitHub OIDC token。这能在发布任何新包版本前发现缺失或不匹配的 npm Trusted Publisher 设置。 - 每个可发布包设置 `publishConfig.provenance=true`,publish step 设置 `NPM_CONFIG_PROVENANCE=true`,publish script 显式给 `pnpm publish` 传 `--provenance`。 - `npm whoami` 对 Trusted Publisher preflight 没有帮助,因为 OIDC authentication 只在 publish 操作期间存在。 npm 要求 package provenance 来自公开源码仓库,并且 package `repository` metadata 必须匹配该 source repo。 Commits 不会触发发布。发布流程是:先用 `pnpm version-packages` 消费 pending changesets,提交版本化后的 package metadata,并让该 commit 通过正常 CI。版本 commit 到 `main` 后,创建并推送 release tag,例如 `v0.0.3`;`.github/workflows/release.yml` 只监听 `v*` tag pushes,并拒绝提交不在 `origin/main` 可达范围内的 tags。Release job 会在还有 pending changesets 时阻塞,为每个 unpublished package 验证 npm OIDC access,运行 `pnpm release:publish`,用 `pnpm publish --provenance` 发布每个尚未发布的 workspace package,然后用幂等的 `changeset tag` 命令创建 package release tags,再推送 `@loggerjs/*` package tags。 如果 npm 在 publish 或 `pnpm release:publish:preflight` 期间返回认证错误,检查每个 package 的 Trusted Publisher 设置是否精确匹配 repository owner、repository name、workflow filename、可选 environment name 和 allowed action。npm 只在交换 OIDC access 或尝试 publish 时检查这些字段。Publish script 对重跑是幂等的:已发布的 package versions 会被跳过,再继续发布剩余包。 参考: - https://docs.npmjs.com/trusted-publishers/ - https://docs.npmjs.com/generating-provenance-statements/ ## 相关链接 - [英文原文](/RELEASE) - [中文首页](/zh/) --- # 测试清单 Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/TEST-INVENTORY.md --- title: "测试清单" description: "测试覆盖清单和验证入口。" --- # 测试清单 本文件由 Vitest JSON reporter 生成,让仓库文档引用一个统一的测试计数来源,而不是手工维护数字。 新增、删除或重命名测试后重新生成: ```bash pnpm test:inventory ``` CI 漂移检查: ```bash pnpm test:inventory:check ``` ## 当前快照 | 指标 | 数量 | | --- | ---: | | Test files | 99 | | Test cases | 664 | | Passed | 664 | | Failed | 0 | | Pending | 0 | | Todo | 0 | | Status | passed | ## 包拆分 | Package | Test files | Test cases | | --- | ---: | ---: | | @loggerjs/browser | 24 | 111 | | @loggerjs/cloudwatch | 1 | 11 | | @loggerjs/codecs | 2 | 39 | | @loggerjs/core | 20 | 232 | | @loggerjs/database | 1 | 6 | | @loggerjs/datadog | 1 | 7 | | @loggerjs/elastic | 1 | 10 | | @loggerjs/loki | 1 | 7 | | @loggerjs/node | 21 | 83 | | @loggerjs/otel | 3 | 13 | | @loggerjs/pretty | 3 | 16 | | @loggerjs/processors | 20 | 117 | | @loggerjs/sentry | 1 | 12 | ## 相关链接 - [英文原文](/TEST-INVENTORY) - [中文首页](/zh/) --- # 基线 Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/BASELINE.md --- title: "基线" description: "当前项目基线和质量状态摘要。" --- # LoggerJS 基线 > 记录于 2026-06-11,用于 `plan.md` 的 Phase 0。 ## 命令 ```bash pnpm install --frozen-lockfile pnpm typecheck pnpm test pnpm build ``` ## 结果 - `pnpm install --frozen-lockfile`:通过。lockfile 已经是最新。 - `pnpm typecheck`:通过 Turbo 覆盖 8 个 workspace packages。 - `pnpm test`:通过 Turbo,但当时 package test commands 使用 `vitest run --passWithNoTests`;这个基线时还没有真实 test files。 - `pnpm build`:packages 和 browser example 通过 Turbo 构建。 ## 基线风险 仓库能 build 和 typecheck,但此时测试信号有意保持很弱。Phase 0 必须先补有意义的测试,再开始 core hot-path rewrite。 ## 相关链接 - [英文原文](/BASELINE) - [中文首页](/zh/) --- # 生产强化提案 Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/PROPOSAL-PRODUCTION-HARDENING.md --- title: "生产强化提案" description: "生产可靠性、观测和交付强化计划。" --- # 生产强化:来自 Pino 的经验 **状态:** 实现记录和剩余路线图 **日期:** 2026-06-13 **范围:** `@loggerjs/core`、`@loggerjs/node`、`@loggerjs/browser`、`@loggerjs/codecs`、vendor transports 和 docs 本文不再是实现前提案。它记录了已经接受的 Pino 经验、已经落地的 hardening 工作,以及仍然剩下的较小工作集。权威 API surface 位于 package TypeScript declarations 和 `api-reports/`;面向用户的运维行为记录在 `docs/CONCEPTS.md`、`docs/TRANSPORTS.md`、`docs/OPERATIONS.md` 和 package READMEs。 ## 1. 论点 LoggerJS **不应该** 采用 Pino 那种“core 直接从调用参数组装 NDJSON line”的模型。这个取舍已经在 `docs/ARCHITECTURE.md` 和 `README.md` 中决定:LoggerJS 每条日志分配一个 `LogRecord`,让 middleware、integrations、routing 和多个 transports 观察同一个结构化事件。序列化仍通过 codecs 由 transport 拥有。 Pino 的有用经验是它的生产工程纪律:可预测 destination lifecycles、显式 flush 和 exit semantics、worker failure boundaries、诚实可靠性文档,以及低摩擦迁移路径。LoggerJS 应保留自己的同构结构化管线,但把运维契约做得像生产 Node logger 一样明确。 ## 2. 已接受边界 - Provider、format、platform-specific 行为保留在 transport、codec、processor 或 integration packages 中。Core 定义 contracts。 - 保留 hot path allocation model。共享 `LogRecord` 管线仍是默认;codec/transport-owned prepared encoders 可接受,因为它们复用稳定片段,但不让 core 拥有 serialization。 - 普通 `logger.info()` 调用不等待 transport startup。Readiness 通过 `logger.ready()` / `transport.ready()` 显式表达。 - 除非目标 destination 已 acknowledge payload,否则把 browser delivery 视为 best effort。 - 优先使用类型安全、tree-shakeable、传对象的 worker APIs,而不是 Pino 风格 runtime string module resolution。 - Core packages 中 redaction 保持 interpreter-based。不用 `eval` 或 `new Function` 编译用户 paths。 ## 3. 当前实现快照 | 领域 | 当前 LoggerJS 状态 | 剩余关注点 | | --- | --- | --- | | Transport lifecycle | `Transport` 包含 `ready`、`flush`、`flushSync` 和 `close`;`Logger.ready()` 是 opt-in;core 有 `close()` 时调用它,只有 transport 没有 `close()` 时才回退到 `flush()`。 | 保持新 transports 与 `docs/CONCEPTS.md` 和 `docs/TRANSPORTS.md` 中的 contract 对齐。 | | Reliability posture | `docs/TRANSPORTS.md` 已分类 immediate、batched、retried、durable queue、page-exit、worker、vendor 和 database paths。 | 新增 vendor transports 时保持 examples 诚实。Raw vendor wire transports 应保持可组合,但必须展示 production wrappers。 | | Pino migration | `@loggerjs/codecs` 包含 `pinoCompatCodec()` / `pinoNdjsonProjector()`,并用 common-field tests 对照 Pino;快速 LoggerJS 输出仍在 `fastEventJsonCodec`。 | 兼容性有意限定在 common NDJSON shape,不覆盖每个 Pino serializer/formatter edge case。 | | Binary codec | `msgpackrCodec()` 基于真实 `msgpackr` 依赖,同时仍允许自定义 pack/unpack injection。 | 在 size reports 中保持 dependency 和 bundle-size impact 可见。 | | Browser loss windows | Browser HTTP、offline queue、IndexedDB、WebSocket、Service Worker 和 BroadcastChannel 的 loss windows 已文档化。`browserServiceWorkerTransport()` 在 `target: "ready"` 时暴露 `ready()`。 | Browser behavior 仍需要周期性真实浏览器验证,因为 quota、Storage Buckets、beacon 和 lifecycle behavior 因浏览器/版本而异。 | | IndexedDB throughput | `indexedDbTransport()` 支持 relaxed durability hints、可选 Storage Buckets、TTL/count/byte pruning、stats,以及 buckets 不可用时显式 fallback 到普通 IndexedDB。 | 吞吐取决于浏览器支持和 storage policy。声明广泛 browser parity 前先使用 benchmarks 和 feature detection。 | | Node destinations | `stdoutTransport`、`stderrTransport`、`fileTransport` 和 `rotatingFileTransport` 在适用处共享内部 Node destination path。它跟踪 callbacks/drain、可选 `minLength`、stream errors、`EPIPE`、sync file mode 和 crash-path `flushSync`。 | Async file mode 中的 `flushSync()` 是 crash-path primitive;如果进程继续,原 async stream 可能仍完成。文档已说明。 | | Fatal crash contract | `captureProcessIntegration({ exitOnUncaught })` 捕获 fatal record,调用 `flushSync()`,等待有界 async `flush()`,然后退出。`docs/OPERATIONS.md` 和测试覆盖该序列,包括 child-process fixture。 | Remote HTTP/vendor transports 不应是唯一 fatal-path sink。 | | Worker lifecycle | `workerTransport()` 支持可选 ready timeout、batch ack timeout、fallback/drop handling、pending-batch gauges、diagnostics、message listeners、close-time flush before listener removal 和 `ready()`。 | Worker API 仍传对象。Pino-style `target` / `targets` module hosting 延后,除非用户更需要 runtime module resolution 而不是类型安全。 | | Diagnostics | `installLoggerDiagnosticsChannel()` 把 LoggerJS internal stages 发布到 `diagnostics_channel`。Stage-specific `hasSubscribers` gating 避免未订阅阶段的工作。 | 每个 package 的 transport-wide metric names 尚未完全统一。 | | Redaction | `redactProcessor()` 文档化 exact keys、paths、`censor` alias、`remove`、depth limits 和 no code generation。`privacyGuardProcessor()` 仍是广谱 PII safety net。 | 热日志器避免 broad regex/deep traversal,除非有测量。 | ## 4. 相比原提案的变化 ### 已实现 1. **Lifecycle contract:** `ready()` 是 public transport shape 的一部分,core 暴露 `logger.ready()`,但不阻塞普通 logging。 2. **Close semantics:** transport-specific `close()` 返回 `void` 后,core 不再额外调用 `flush()`;拥有资源的 transports 拥有自己的 flush-before-release 行为。 3. **Reliability docs:** transport posture 和 browser loss windows 已明确。 4. **Pino compatibility codec:** 迁移用户可以 opt into Pino-shaped NDJSON,而不改变默认 LoggerJS envelope。 5. **Redaction clarity:** 接受 Pino-friendly `censor` 命名作为 alias,同时 LoggerJS 保留安全 interpreter-based matching。 6. **Shared Node destination:** stream/file transports 共享 backpressure、buffering、stream error、EPIPE、close 和 crash-flush 行为。 7. **Fatal-path tests and docs:** process crash behavior 已文档化和测试。 8. **Worker lifecycle:** ready/ack protocol、fallback/drop behavior 和 close-time flush 已实现。 9. **Internal diagnostics:** LoggerJS 通过 gated sink 和 Node `diagnostics_channel` bridge 发出可选 diagnostic stages。 ### 部分实现 1. **Consistent transport metrics:** queue depth、worker readiness、worker drops、batch drops 和许多 browser counters 已存在,但并非每个 transport 都使用单一文档化命名矩阵。 2. **Runtime compatibility gate:** 仓库有 package smoke tests 和 Bun/Deno/Workers smoke script,但本地验证依赖外部 runtimes 已安装。 3. **Browser IndexedDB performance proof:** 实现已有 relaxed durability 和 Storage Buckets 等现代浏览器 knobs,但性能声明应限定到已测 browser/version combinations。 ### 延后 1. **Numeric compatibility 之外的 custom levels:** 对 syslog/vendor mapping 有用,但会触及 OTLP mapping、routing、processors、typed events 和 benchmark semantics。设计优先。 2. **Disabled-method noop replacement:** 只有 benchmark 证明相对当前 level gate 有真实收益,且能保留 child loggers、dynamic level changes 和 TypeScript ergonomics 时才做。 3. **Pino-style worker module host:** 除非 runtime module resolution 比类型安全和 bundler friendliness 更有价值,否则延后。 4. **Generated redaction compiler:** 不加入 core。只有在有测量需求和清晰安全文档时,才考虑作为 optional package。 ## 5. 剩余路线图 ### 5.0 在基线足够无聊前停止扩 surface **原因:** 当前 first-party surface 已经很宽。比新增 transports 和 integrations 更有价值的是让现有能力在用户依赖的 runtimes 中可预测、可文档化、可验证。 v1 前默认路径: - 先强化现有生产路径,再新增路径; - 保持 runtime-specific behavior 在 `@loggerjs/core` 外部; - 只有同时具备 stability docs、import-boundary docs、size-budget evidence 和真实环境验证时,才新增 public component subpaths; - 当组合现有 processors/transports 能解决用例时,优先写 examples 而不是新增 API。 **完成标准:** `pnpm check`、browser E2E、live integration config/readiness 和 release docs 对 public component 是否可发布给出同一个答案。 ### 5.1 完成 Transport Metric 命名 **原因:** 用户需要跨 Node、browser、database 和 vendor transports 的单一 degradation 心智模型。 推荐稳定名称: - `transport.dropped` - `transport.dropped.` - `transport.errors` - `transport.queue.depth.` - `transport.backpressure.` - `transport.ready.` - `transport.circuit.open.` 避免在 metric names 中放 destination URLs、file paths、tenant ids、request ids、category names 或其他高基数值。 **完成标准:** 内置 transports 在适用处使用文档化名称,`getLoggerSelfMetrics()` 示例匹配实现,且每个 runtime family 至少一个 transport 有测试覆盖。 ### 5.2 增加测量过的 Browser IndexedDB 结果 **原因:** IndexedDB transport 有有用的高性能选项,但 browser storage performance 依赖版本和设备。 至少测量: - Regular IndexedDB durability vs `durability: "relaxed"`。 - Storage Buckets available vs unavailable。 - Single-tab batch writes 和 multi-tab contention。 - 可行时测 quota pressure 或 blocked upgrades 下的 flush latency。 **完成标准:** `docs/BENCHMARKS.md` 或 browser-specific benchmark doc 列出 browser versions、hardware、scenario 和 caveats。避免在未测环境外写 broad “fastest” claims。 ### 5.3 保持 Runtime Smoke Coverage 可运行 **原因:** LoggerJS 明确是同构的。CI 应保持 browser-like 和 non-Node runtimes 诚实。 当前 smoke script 支持: - `--runtime=bun` - `--runtime=deno` - `--runtime=workers` / `workerd` / `miniflare` **完成标准:** CI 安装所需 runtime binaries,并为声称 cross-runtime support 的 packages 运行 smoke script。本地缺少 runtime 造成的失败应报告为环境缺口,而不是 package regression。 ### 5.4 扩展 Vendor Reliability 示例 **原因:** Raw vendor transports 设计上保持可组合,但生产用户需要显式展示 retry、batching 和 fallback 的示例。 每个新 vendor transport 都应包含: - raw minimal example; - 使用 `batchTransport()` 或 `retryTransport()` 的 production wrapper example; - failure behavior:retryable status codes、permanent drops、queue limits 和 credential/runtime constraints。 **完成标准:** vendor docs 不暗示 raw delivery durable 或 retried,除非 transport 自己真的拥有这些语义。 ## 6. 验证清单 未来 hardening changes 使用这份清单: - API changes:运行 typecheck、tests、API report check 和 package smoke tests。 - Hot-path changes:运行 `pnpm bench:gate`,只用测量结果更新文档 ratios。 - Node destination changes:运行 node transport tests、crash/exit fixtures、EPIPE tests 和 size checks。 - Worker changes:测试 ready timeout、ack timeout、worker error、non-zero exit、fallback delivery、`flush()`、`close()` 和 `autoEnd`。 - Browser transport changes:在声明 durability 前测试 feature fallback paths 并文档化 loss windows。 - Docs-only changes:代码落地后扫描 stale “currently missing” wording。 ## 附录:映射到早前 12 点 review | Review # | Topic | Current disposition | | --- | --- | --- | | 1 | Worker transport host | Lifecycle protocol 已实现;module-host model 仍延后。 | | 2 | SonicBoom-style destination | 已实现 shared Node destination,没有引入 SonicBoom runtime dependency。 | | 3 | Transport lifecycle contract | 已在 core 中实现,并在 concepts/transports 中文档化。 | | 4 | Multi-target routing semantics | 通过 route processors、transport names 和 reliability docs 覆盖;未来 routing changes 仍需 benchmark。 | | 5 | Pino-compatible codec | 已在 `@loggerjs/codecs` 实现。 | | 6 | Redaction path compiler | 已实现安全文档和 aliases;generated compiler 仍延后。 | | 7 | Custom levels | 延后,设计优先。 | | 8 | Disabled-method noop | 延后,用 benchmark gate。 | | 9 | `diagnostics_channel` | 已实现,并带 stage-specific subscription gating。 | | 10 | Fatal / crash flush | 已文档化,并用 sync + bounded async flush 测试。 | | 11 | Browser console ergonomics | 已并入 browser transport 和 loss-window docs;未来 polish 应继续 privacy-first。 | | 12 | Documentation discipline | 持续要求:docs 必须区分 measured behavior、best-effort delivery 和 design intent。 | ## 相关链接 - [英文原文](/PROPOSAL-PRODUCTION-HARDENING) - [中文首页](/zh/) --- # AI Skill Source: https://github.com/jskits/loggerjs/blob/main/docs/zh/AI-SKILL.md --- title: "AI Skill" description: "安装并使用 LoggerJS AI skill,让 coding agent 直接完成 LoggerJS 接入。" --- # AI Skill LoggerJS 提供可安装的 AI skill,供 coding agent 使用。这个 skill 会指导 agent 选择 LoggerJS 包、按运行时新增 logger 模块、配置生产日志、从现有 logger 迁移,并在目标项目里完成验证。 当你希望 agent 直接把 LoggerJS 接入应用,而不是只阅读文档时,使用这个 skill。 ## 安装 从 GitHub 仓库安装: ```bash npx skills add jskits/loggerjs --skill loggerjs ``` 安装到指定 agent: ```bash npx skills add jskits/loggerjs --skill loggerjs --agent codex npx skills add jskits/loggerjs --skill loggerjs --agent claude-code ``` 不安装,单次使用: ```bash npx skills use jskits/loggerjs --skill loggerjs ``` 从本地 checkout 开发或测试: ```bash npx skills add . --skill loggerjs npx skills use . --skill loggerjs ``` ## 包含内容 - `skills/loggerjs/SKILL.md`:接入或迁移 LoggerJS 的主流程。 - `skills/loggerjs/references/package-selection.md`:包和运行时选择规则。 - `skills/loggerjs/references/runtime-recipes.md`:Node、浏览器、library、pretty、OpenTelemetry 和 Sentry 配方。 - `skills/loggerjs/references/production-checklist.md`:隐私、可靠性、生命周期、性能和 vendor 投递护栏。 - `skills/loggerjs/references/migration.md`:从 console、pino、winston、loglevel、debug 和现有 wrapper 迁移。 - `skills/loggerjs/references/troubleshooting.md`:常见投递、flush、浏览器、codec 和 TypeScript 问题。 - `skills/loggerjs/scripts/inspect-loggerjs-project.mjs`:只读项目识别脚本,根据目标应用推荐 LoggerJS 包。 ## 示例 prompt ```text Use $loggerjs to add production-ready structured logging to this Node API. ``` ```text Use $loggerjs to migrate this React app from console warnings/errors to LoggerJS browser logging. ``` ```text Use $loggerjs to review this service's existing pino setup and propose the smallest safe LoggerJS migration. ``` ## 和 llms.txt 的关系 skill 本身保持短而可执行。需要精确 API 或更完整设计上下文时,它会指向面向 LLM 的文档: - [llms.txt](/zh/llms.txt):中文文档地图。 - [llms-full.txt](/zh/llms-full.txt):适合更大上下文窗口的扩展文档和 skill 上下文。 - [参考](/zh/reference/):生成的包、API 和示例参考。 --- # LoggerJS AI Skill Source: https://github.com/jskits/loggerjs/blob/main/skills/loggerjs/SKILL.md --- name: loggerjs description: Use LoggerJS in JavaScript or TypeScript projects. Trigger when adding structured logging, choosing LoggerJS packages for Node.js, browser, worker, edge, CLI, or library code, configuring transports, integrations, processors, codecs, OpenTelemetry or vendor delivery, migrating from console, pino, winston, loglevel, debug, or other loggers, or troubleshooting LoggerJS logging behavior. --- # LoggerJS ## Overview Use this skill to integrate LoggerJS into a real JavaScript or TypeScript codebase with the smallest correct package set, a runtime-appropriate logger module, production guardrails, and repo-local validation. LoggerJS is an isomorphic structured logging toolkit: a zero-dependency core plus platform packages for Node.js, browsers, workers, edge runtimes, transports, integrations, processors, codecs, and vendor delivery. ## Workflow 1. Inspect the target project before changing code. - Run `node /scripts/inspect-loggerjs-project.mjs ` when the skill files are available locally. - Otherwise inspect `package.json`, lockfiles, framework config, existing logger dependencies, and current logging call sites manually. 2. Choose the minimum package set. - Read `references/package-selection.md` when runtime, package, or vendor choice is not obvious. - Prefer `@loggerjs/node` for Node services/CLIs, `@loggerjs/browser` for frontend apps, and `@loggerjs/core` for runtime-neutral libraries. 3. Add one centralized logger module per runtime boundary. - Keep app code importing from the local logger module, not directly from many LoggerJS package paths. - Platform packages re-export core APIs, so start with one platform package unless a vendor or processor package is needed. 4. Configure production safety before broad rollout. - Read `references/production-checklist.md` before adding browser delivery, vendor delivery, persistent queues, or automatic capture. - Add redaction/privacy processors before transports that leave the process. - Never place vendor API keys, long-lived tokens, or private ingestion credentials in browser code. 5. Migrate incrementally. - Read `references/migration.md` when replacing console, pino, winston, loglevel, debug, consola, or a custom wrapper. - Preserve existing call-site semantics first, then improve structure, context, and delivery. 6. Validate with the target repo's own gates. - Run the package manager install command if dependencies changed. - Run typecheck/build/tests or the closest project-local equivalents. - For browser apps, verify no secret-bearing environment variables are bundled client-side. ## Reference Map - `references/package-selection.md`: package matrix, runtime detection, install commands, and vendor package choices. - `references/runtime-recipes.md`: minimal Node, browser, library, local pretty, OpenTelemetry, and Sentry recipes. - `references/production-checklist.md`: privacy, reliability, lifecycle, performance, and browser credential guardrails. - `references/migration.md`: incremental migration patterns from existing loggers. - `references/troubleshooting.md`: common missing-log, duplicate-log, browser, flush, codec, and delivery failures. Use the public docs when exact API details or broader context are needed: - Concise map: `https://jskits.github.io/loggerjs/llms.txt` - Full context: `https://jskits.github.io/loggerjs/llms-full.txt` - Package/API reference: `https://jskits.github.io/loggerjs/reference/` ## Implementation Rules - Prefer TypeScript examples and ESM imports. - Keep LoggerJS setup in a small module such as `src/logger.ts`, `src/lib/logger.ts`, or the framework's existing observability/logging module. - Keep disabled-level hot paths cheap: avoid eager string interpolation, `JSON.stringify`, expensive context builders, or stack parsing before the logger level gate. - Use lazy messages or structured data for expensive values. - Use processors only when event-level behavior is needed. No-processor record paths are the fastest path. - Treat integrations as opt-in automatic capture. Add only the platform hooks the app actually needs. - Treat codecs as transport-owned. Do not pre-stringify records in application code before LoggerJS sees them. - Pair remote transports with bounded batching, retry/backoff, circuit-breakers, or offline queues where appropriate. - Make `flush()` part of controlled shutdown, tests that assert delivery, CLI exit paths, and requestless scripts. - In libraries, prefer `getLogger(["package-or-feature"])`; do not force host applications to configure transports. ## Minimal Patterns Node service: ```ts import { captureProcessIntegration, createLogger, stdoutTransport } from "@loggerjs/node"; import { redactProcessor } from "@loggerjs/processors"; export const logger = createLogger({ name: "api", level: process.env.LOG_LEVEL ?? "info", tags: { service: "api", env: process.env.NODE_ENV ?? "dev" }, processors: [redactProcessor()], transports: [stdoutTransport()], integrations: [captureProcessIntegration()], }); ``` Browser app: ```ts import { browserHttpTransport, captureBrowserErrorsIntegration, captureFetchIntegration, createLogger, pageLifecycleIntegration, } from "@loggerjs/browser"; import { redactProcessor } from "@loggerjs/processors"; export const logger = createLogger({ name: "web", level: "info", processors: [redactProcessor()], transports: [ browserHttpTransport({ url: "/api/logs", maxBatchSize: 20, flushIntervalMs: 1500, useBeaconOnPageHide: true, }), ], integrations: [ captureBrowserErrorsIntegration(), captureFetchIntegration(), pageLifecycleIntegration(), ], }); ``` Library code: ```ts import { getLogger } from "@loggerjs/core"; const logger = getLogger(["my-library"]); export function doWork() { logger.debug("work started"); } ``` ## Output Expectations When reporting the result, include the files changed, the selected LoggerJS packages, the runtime assumptions, and the validation commands that passed or could not be run. --- # LoggerJS AI Skill Package Selection Source: https://github.com/jskits/loggerjs/blob/main/skills/loggerjs/references/package-selection.md # Package Selection Use the smallest package set that matches the target runtime and delivery path. ## Runtime Matrix | Target | Start with | Add when needed | | --- | --- | --- | | Node service, API, worker process, CLI | `@loggerjs/node` | `@loggerjs/processors`, `@loggerjs/pretty`, `@loggerjs/otel`, vendor transports | | Browser SPA, frontend route, web widget | `@loggerjs/browser` | `@loggerjs/processors`, browser-safe vendor proxy transport | | Shared library with no delivery ownership | `@loggerjs/core` | Usually none; the host app owns transports | | Isomorphic app with separate client/server bundles | `@loggerjs/node` on server and `@loggerjs/browser` on client | Shared local wrapper types if needed | | Edge or worker runtime without Node APIs | `@loggerjs/core` or `@loggerjs/browser` if browser APIs exist | Custom fetch transport or platform-specific delivery | | Local developer pretty logs | Runtime package plus `@loggerjs/pretty` | Keep production structured transport separate | ## Common Add-ons | Need | Package | | --- | --- | | Redaction, sampling, routing, dedupe, fingerprinting, buffering | `@loggerjs/processors` | | MessagePack or richer serialization helpers | `@loggerjs/codecs` | | OpenTelemetry trace mapping or OTLP HTTP logs | `@loggerjs/otel` | | Sentry logs, breadcrumbs, or captured exceptions | `@loggerjs/sentry` | | Datadog Logs API | `@loggerjs/datadog` | | Elasticsearch bulk indexing | `@loggerjs/elastic` | | Grafana Loki push API | `@loggerjs/loki` | | Amazon CloudWatch Logs | `@loggerjs/cloudwatch` | | SQLite, PostgreSQL, or custom database sink | `@loggerjs/database` | ## Install Commands Use the package manager already used by the repo. ```bash npm install @loggerjs/node @loggerjs/processors pnpm add @loggerjs/node @loggerjs/processors yarn add @loggerjs/node @loggerjs/processors bun add @loggerjs/node @loggerjs/processors ``` For browsers: ```bash npm install @loggerjs/browser @loggerjs/processors ``` For libraries: ```bash npm install @loggerjs/core ``` ## Selection Rules - If the project has `express`, `fastify`, `koa`, `hapi`, `nestjs`, `prisma`, `bullmq`, or Node server scripts, choose `@loggerjs/node`. - If it has `react`, `vue`, `svelte`, `vite`, `next`, `nuxt`, `angular`, `astro`, browser entry files, or `window`/`document` logging, choose `@loggerjs/browser` for the client bundle. - If it has both SSR/server and browser code, configure separate logger modules for server and client. - If the package is a published library, has `exports` or `main`, and should not decide delivery, use `@loggerjs/core` with `getLogger()`. - If migrating from an existing logger, install LoggerJS beside it first and preserve the old API through a local adapter when that reduces call-site churn. - Do not install every vendor package up front. Add vendor transports only when the destination is known and credentials/configuration exist. ## Credential Boundaries - Browser code may send to your own ingestion endpoint, not directly to services that require private ingestion credentials. - Node services may own vendor credentials through server-side environment variables. - For edge runtimes, confirm whether the platform permits the needed network APIs and environment-secret model before adding vendor delivery. --- # LoggerJS AI Skill Runtime Recipes Source: https://github.com/jskits/loggerjs/blob/main/skills/loggerjs/references/runtime-recipes.md # Runtime Recipes Use these as starting points, then adjust names, tags, transports, and integrations to the target app. ## Node Service ```ts import { captureProcessIntegration, createLogger, stdoutTransport } from "@loggerjs/node"; import { redactProcessor, tagsProcessor } from "@loggerjs/processors"; const logger = createLogger({ name: "api", level: process.env.LOG_LEVEL ?? "info", tags: { service: "api", env: process.env.NODE_ENV ?? "dev" }, processors: [redactProcessor(), tagsProcessor({ runtime: "node" })], transports: [stdoutTransport()], integrations: [captureProcessIntegration()], }); logger.info("server started", { port: 3000 }); await logger.flush(); ``` Use stdout first for container platforms that already collect process output. Add OTLP, Loki, Datadog, CloudWatch, or database transports only when the deployment needs direct delivery. ## Browser App ```ts import { browserHttpTransport, captureBrowserErrorsIntegration, captureConsoleIntegration, captureFetchIntegration, createLogger, pageLifecycleIntegration, } from "@loggerjs/browser"; import { redactProcessor, sampleProcessor } from "@loggerjs/processors"; export const logger = createLogger({ name: "web", level: "info", tags: { app: "web" }, processors: [ redactProcessor(), sampleProcessor({ rates: { trace: 0.05, debug: 0.2, info: 1, warn: 1, error: 1, fatal: 1 } }), ], transports: [ browserHttpTransport({ url: "/api/logs", maxBatchSize: 20, flushIntervalMs: 1500, useBeaconOnPageHide: true, }), ], integrations: [ captureConsoleIntegration({ levels: ["warn", "error"] }), captureBrowserErrorsIntegration(), captureFetchIntegration(), pageLifecycleIntegration(), ], }); ``` Use a server-owned ingestion endpoint for browser logs. Add IndexedDB/offline persistence when reload survival matters and the app can tolerate storage quota failures. ## Library ```ts import { getLogger } from "@loggerjs/core"; const logger = getLogger(["my-library"]); export function parseInput(value: unknown) { logger.debug("parse input", { valueType: typeof value }); } ``` Library code should not configure transports globally. Let the host app call `configure()` or create application loggers. ## Local Pretty Output ```ts import { createLogger } from "@loggerjs/node"; import { prettyStdoutTransport } from "@loggerjs/pretty"; export const logger = createLogger({ name: "dev", level: "debug", transports: [prettyStdoutTransport()], }); ``` Keep pretty output for local development. Production should still have structured delivery. ## OpenTelemetry Delivery ```ts import { createLogger, stdoutTransport } from "@loggerjs/node"; import { openTelemetryTraceProcessor, otlpHttpTransport } from "@loggerjs/otel"; export const logger = createLogger({ name: "api", processors: [openTelemetryTraceProcessor()], transports: [ stdoutTransport(), otlpHttpTransport({ url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ?? "http://localhost:4318/v1/logs", resource: { "service.name": "api" }, }), ], }); ``` Use this when the app already has OpenTelemetry tracing or an OTLP collector. ## Sentry Delivery ```ts import { createLogger } from "@loggerjs/core"; import { sentryTransport } from "@loggerjs/sentry"; import * as Sentry from "@sentry/node"; export const logger = createLogger({ name: "api", transports: [sentryTransport({ sentry: Sentry, captureMessages: true })], }); ``` Use Sentry as an error/alerting path, not as the only high-volume structured-log sink unless the project has accepted that cost and retention model. --- # LoggerJS AI Skill Production Checklist Source: https://github.com/jskits/loggerjs/blob/main/skills/loggerjs/references/production-checklist.md # Production Checklist Apply this before broad rollout or before adding automatic capture. ## Privacy - Add `redactProcessor()` or a stricter privacy processor before any remote transport. - Redact tokens, cookies, authorization headers, passwords, secrets, session IDs, and user-provided free-form fields. - Keep raw request/response bodies out of default logs unless the app has a clear data classification rule. - In browser code, assume all bundled environment variables and network payloads are inspectable by users. ## Reliability - Use bounded queues. Choose an explicit drop policy instead of allowing unbounded memory growth. - Pair network transports with batch size, flush interval, retry/backoff, and circuit-breaker settings appropriate to the destination. - Call `flush()` on controlled shutdown, CLI exit, worker teardown, and tests that assert delivery. - Use crash-path synchronous flush only for local process-safe transports; remote network delivery is best-effort during crashes. - Track LoggerJS self-metrics, drop counters, and transport errors in the app's existing observability path. ## Browser Lifecycle - Enable page lifecycle integration when logs matter during tab close or navigation. - Treat `sendBeacon` as a last-chance attempt, not a delivery guarantee. - Use IndexedDB/offline queues when reload survival matters, but handle quota, private browsing, eviction, and blocked upgrades. - Prefer sampling or rate limits for high-volume browser debug/info events. ## Performance - Keep expensive message construction behind the logger level gate. - Prefer structured data over preformatted strings when fields are later redacted, routed, or indexed. - Use processors only when event-level behavior is required; processor use disables the pure record fast path. - Keep hot-path browser integrations scoped to needed signals. - Benchmark if changing codec, batching, sampling, or high-volume call sites. ## Vendor Delivery - Server-side vendor packages may use private credentials from environment variables. - Browser logs should flow through an application-owned ingestion endpoint or a public-safe token model. - Name the target service and region/site explicitly for Datadog, CloudWatch, Loki, Elasticsearch, Sentry, or OTLP. - Add a fallback or secondary transport when delivery is operationally critical. --- # LoggerJS AI Skill Migration Source: https://github.com/jskits/loggerjs/blob/main/skills/loggerjs/references/migration.md # Migration Migrate in slices. Keep existing call-site behavior working first, then improve structure. ## From `console` 1. Add a local logger module. 2. Replace new or touched `console.log`, `console.warn`, and `console.error` calls with `logger.info`, `logger.warn`, and `logger.error`. 3. For broad capture, add console integration with a narrow level set such as `["warn", "error"]`. 4. Keep debug/info console capture off by default in production unless sampled. Example: ```ts logger.info("order created", { orderId }); logger.error(error, "payment failed", { orderId }); ``` ## From pino Map these first: | pino concept | LoggerJS direction | | --- | --- | | `level` | `level` in `createLogger()` | | bindings/base fields | `tags`, context provider, or local wrapper | | serializers | processor or middleware | | destination/transport | LoggerJS transport | | `child()` | separate named/category logger or wrapper adding tags/context | | `flush()` | `await logger.flush()` | Preserve the existing wrapper API if many call sites use pino-specific argument ordering. Replace internals first, then gradually convert call sites to structured LoggerJS calls. ## From winston Map these first: | winston concept | LoggerJS direction | | --- | --- | | format pipeline | processors/middleware plus transport-owned codec | | transports array | LoggerJS transports array | | exception/rejection handlers | Node process integration | | defaultMeta | tags or context provider | | custom format redaction | `redactProcessor()` or privacy processor | Do not pre-stringify in a format step before LoggerJS. Keep raw fields available for redaction, routing, and transport-specific codecs. ## From loglevel/debug/consola/tslog - Keep the same public wrapper names if they are widely used. - Map namespaces to `name` or category tags. - Replace string-only calls with structured data where the surrounding code already has field values. - Use `@loggerjs/pretty` for local developer ergonomics when the old logger was mainly human-readable. ## Safe Rollout Pattern 1. Add LoggerJS alongside the old logger. 2. Create an adapter with the old logger's common methods. 3. Send to stdout or a test/memory transport first. 4. Add redaction and production transport. 5. Switch one module or route at a time. 6. Remove the old logger dependency only after call sites and tests no longer import it. --- # LoggerJS AI Skill Troubleshooting Source: https://github.com/jskits/loggerjs/blob/main/skills/loggerjs/references/troubleshooting.md # Troubleshooting ## No Logs Appear - Confirm the logger `level` allows the emitted level. - Confirm at least one transport is configured. - Confirm the app imports the configured local logger, not a fresh unconfigured logger. - In libraries using `getLogger()`, confirm the host app configured the registry. - Await `logger.flush()` in scripts, tests, and short-lived processes. ## Duplicate Logs - Check whether both direct LoggerJS calls and console integration are capturing the same message. - Ensure integration setup runs once per app lifecycle, not once per request or React render. - In hot reload/dev servers, keep teardown logic or guard initialization with module-level state. ## Browser Delivery Fails - Check network status, CORS, ingestion endpoint path, response status, and content type expectations. - Do not send browser logs directly to private vendor APIs that require secret credentials. - Enable page lifecycle flush for navigation/tab-close cases. - Use an offline queue only after deciding quota and drop behavior. ## Process Exits Before Delivery - Await `logger.flush()` before `process.exit()`. - For CLIs, set `process.exitCode` and return instead of calling `process.exit()` immediately. - Use stdout/file transports for crash-path reliability; remote network transports are best-effort during fatal shutdown. ## Encoding or Serialization Errors - Do not call `JSON.stringify()` on log data before passing it to LoggerJS. - Use built-in codecs or transport-owned codecs so Error, BigInt, circular values, and rich objects can be handled safely. - Check LoggerJS meta counters when payloads are dropped, coerced, or encoded with fallback behavior. ## TypeScript Import Errors - Use public package exports, not `dist` or source-internal paths. - Re-run install after adding a LoggerJS package. - For exact exports, check `https://jskits.github.io/loggerjs/reference/packages` and the package API report.