迁移说明
本页说明如何从 pino、winston 和 console.log 迁移,并介绍从这些 logger 转过来时最容易让人意外的 LoggerJS 约定。
从 pino 迁移
相同 levels、相同 numeric values、相同 NDJSON 直觉,映射大多是机械的。
// 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
basefields 拆为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()(终端)或prettyConsoleTransport()(浏览器 DevTools)承担。core 的consoleTransport()仍然只是基础的本地 console sink。 - 需要 Pino 格式的 NDJSON 时,使用
@loggerjs/codecs的pinoCompatCodec()。把数据合并到根级需要主动开启(mergeData: true),与保留键冲突时默认嵌套存放,而不是覆盖time、level、msg、pid、hostname或err。 - 最快的 LoggerJS lean 信封使用
fastEventJsonCodec({ includeId: false, includeSeq: false, includeLevelName: false })。支持 record 的自定义 transport 可以用createPreparedRecordEncoder(codec)包装它,复用稳定的 logger/tag 片段。在 M1 Max 参考机器上,普通 lean 路径约为 pino 吞吐量的 1.19 倍,prepared lean 路径约为 1.28 倍(配对 A/B;相对 pino 的排名取决于 CPU/V8,见 基准);在此基础上,你还能获得 middleware、integration、多 transport 分发以及同构的浏览器支持。
从 winston 迁移
// 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
formatchains 拆成两个关注点:processors/middleware(数据塑形:redact、enrich、filter)和 codecs(序列化,由每个 transport 拥有)。format.combine(timestamp, json)通常就是默认输出。 defaultMeta->tags和/或bindings。- Per-transport
level直接映射到任何 transport 的minLevel。 - 用 child logger 代替
winston.loggers注册表来做按模块的配置;库作者优先使用 core 的getLogger()。 - 在顺序基准套件中,LoggerJS lean 路径的吞吐量约为 winston 的 10 倍(见 基准)。
从 console.log 迁移
两种迁移方式可以组合使用。
先捕获,逐步迁移:不改调用点,把已有 console calls 转成结构化日志:
import { captureConsoleIntegration, createLogger, browserHttpTransport } from "@loggerjs/browser";
const logger = createLogger({
transports: [browserHttpTransport({ url: "/api/logs" })],
integrations: [captureConsoleIntegration({ levels: ["log", "warn", "error"] })],
});再在值得结构化的调用点替换:
// before
console.log("order created", orderId);
console.error("payment failed", err);
// after
logger.info("order created", { orderId });
logger.error(err, "payment failed");每一步带来的收益:日志级别及按级别过滤、用结构化数据代替字符串插值、数据离开进程前先脱敏、批量/离线投递,以及通过错误/进程 integration 采集崩溃路径。
Middleware 与 Processor
LoggerJS 有两层同步的数据处理,二者都是一等公民:
- Middleware 在计算 id、消息或错误之前运行在原始
LogRecord上。它是补充字段、脱敏或丢弃日志成本最低的位置,并且不会破坏 record 快速路径。 - Processor 运行在投影后的
LogEvent上。需要解析后的 event 结构时使用它,例如路由、指纹或 fingers-crossed 缓冲。只要配置了任意 processor,该 logger 就不再走 record 快速路径。
@loggerjs/processors 同时提供两种形式,例如 tagsMiddleware() 和 tagsProcessor()。自定义 middleware 使用 createMiddleware():
import { createMiddleware } from "@loggerjs/core/middleware";
import { redactProcessor, tagsMiddleware } from "@loggerjs/processors";
const logger = createLogger({
middleware: [tagsMiddleware({ service: "checkout" })],
processors: [redactProcessor()],
});完整模型见 核心概念。
Context
创建 logger 时就已知的 context 用 child logger 绑定:
const requestLogger = logger.child({ bindings: { requestId: "req_123" } });请求作用域使用环境 context:
import { withContext } from "@loggerjs/core";
import { installAsyncLocalStorageContext } from "@loggerjs/node";
installAsyncLocalStorageContext();
await withContext({ requestId: "req_123" }, async () => {
logger.info("request started");
});浏览器 Integration
浏览器采集需要主动开启。在配置对应的 integration 之前,现有的手动日志代码不会捕获 console 调用、错误、fetch 或 XHR。
常见的起步组合:
captureConsoleIntegration({ levels: ["warn", "error"] });
captureBrowserErrorsIntegration();
captureFetchIntegration();
pageLifecycleIntegration();Transport 与 Codec
序列化属于 transport。把 JSON 或字符串格式化从 middleware 和 processor 中移出,交给 transport 的 codec:
browserHttpTransport({ url: "/api/logs", codec: safeJsonCodec() });编写自定义 transport 时,实现 write/writeBatch 可以在快速路径上接收 LogRecord,实现 log/logBatch 则接收投影后的 LogEvent。凡是涉及网络 I/O 的 transport,都应使用 batchTransport() 包装,以获得队列上限、重试、字节限制、并发和熔断能力。见 传输。
从 0.7 升级到 1.0
1.0 删除了 0.7 中标为 deprecated 的内容,并收紧了安装要求。请先升级到最新的 0.7,修复编辑器或 tsc 报告的 deprecation 警告;1.0 删除的每个导出在 0.7 中都已标为 @deprecated。然后:
- 使用 Node 22 或更高版本。 所有包都声明了
engines.node: ">=22.0.0"。 - 自行安装
@loggerjs/core。 其他所有包都把它作为 peer dependency,范围是^1.0.0,以保证应用中只有一份 core,例如npm install @loggerjs/core @loggerjs/node。npm 和 pnpm 会自动安装缺失的 peer,Yarn 不会。基于 LoggerJS 构建的库也应把@loggerjs/core声明为 peer dependency。 - 从子路径导入被移走的导出。 core 根入口只导出内核,browser 和 node 根入口只导出内核加上各自的 stable 组件。下面两节列出了每个被删除导出对应的子路径。
- 同时升级所有
@loggerjs/*包。 它们都是 1.0.0。0.7 的包直接依赖 core 0.7,与 1.0 的包混用会安装第二份 core。
API 稳定性 说明了 1.0 对每个导出的承诺。
1.0 从 core 根入口删除的导出
1.0 起 @loggerjs/core 根入口只导出内核。下列导出在 0.7 标为 deprecated,现已无法从根入口使用;请从对应子路径导入:
| 导出 | 导入路径 |
|---|---|
parseTraceparent、formatTraceparent、parseBaggage、formatBaggage、traceContextFromHeaders、traceContextToHeaders | @loggerjs/core/trace-propagation |
semanticEvents 及 Semantic*Payload 类型 | @loggerjs/core/semantic-events |
applyPayloadTransforms、composePayloadTransforms、encryptionPayloadTransform、encodedPayloadToUint8Array | @loggerjs/core/payload-transforms |
setLoggerDiagnosticSink、runLoggerDiagnostic、emitLoggerDiagnostic、loggerDiagnosticsEnabled、loggerDiagnosticNow | @loggerjs/core/diagnostics |
createIntegrationSetupContext、getUnpatchedRegistry、registerUnpatchedDefaults、onceTeardown | @loggerjs/core/integration-api |
withLogEventRoute、getLogEventRoute、LOGGERJS_ROUTE | @loggerjs/core/event-route |
metricsCodec | @loggerjs/core/codec-metrics |
createPreparedRecordEncoder | @loggerjs/core/codec-prepared |
testTransport 及其选项类型 | @loggerjs/core/transport-test |
Integration、IntegrationSetupContext、Transport、Codec 等管线类型仍留在根入口。
1.0 从 browser 和 node 根入口删除的导出
1.0 起 @loggerjs/browser 和 @loggerjs/node 根入口只导出 core 内核和 stable 组件:浏览器端是 browserHttpTransport、IndexedDB 存储、offlineFirstTransport,以及 console、error、context、page-lifecycle integration;Node 端是 stdoutTransport、stderrTransport、fileTransport、rotatingFileTransport、nodeHttpTransport、进程采集和 AsyncLocalStorage context。根入口的其他导出在 0.7 标为 deprecated,现已删除。请连同它们的选项类型一起从子路径导入:
| 浏览器端导出 | 导入路径 |
|---|---|
browserBroadcastChannelTransport | @loggerjs/browser/transport-broadcast-channel |
browserCompressionPayloadTransform | @loggerjs/browser/payload-transforms |
browserServiceWorkerTransport | @loggerjs/browser/transport-service-worker |
browserWebSocketTransport | @loggerjs/browser/transport-websocket |
createLogZipBlob, downloadBlob, exportLogsToZip | @loggerjs/browser/export-zip |
captureFetchIntegration | @loggerjs/browser/integration-fetch |
captureXHRIntegration | @loggerjs/browser/integration-xhr |
captureFrameworkErrorsIntegration | @loggerjs/browser/integration-framework-errors |
nextRouterIntegration, nuxtRouterIntegration, reactRouterIntegration, vueRouterIntegration | @loggerjs/browser/integration-framework-routers |
captureReportingIntegration | @loggerjs/browser/integration-reporting |
captureRouterIntegration | @loggerjs/browser/integration-router |
captureRuntimeHostIntegration | @loggerjs/browser/integration-runtime-host |
captureServiceWorkerIntegration | @loggerjs/browser/integration-service-worker |
captureUserActionsIntegration | @loggerjs/browser/integration-user-actions |
captureWebSocketIntegration | @loggerjs/browser/integration-websocket |
captureWebVitalsIntegration | @loggerjs/browser/integration-web-vitals |
capturePerformanceIntegration, normalizeBrowserPerformanceEntry | @loggerjs/browser/integration-performance |
| Node 端导出 | 导入路径 |
|---|---|
nodeCompressionPayloadTransform | @loggerjs/node/payload-transforms |
formatSyslogMessage, nodeSyslogTransport | @loggerjs/node/transport-syslog |
workerTransport | @loggerjs/node/transport-worker |
bullMqIntegration | @loggerjs/node/integration-bullmq |
captureCliIntegration | @loggerjs/node/integration-cli |
databaseIntegration | @loggerjs/node/integration-database |
expressIntegration | @loggerjs/node/integration-express |
fastifyIntegration | @loggerjs/node/integration-fastify |
hapiIntegration | @loggerjs/node/integration-hapi |
koaIntegration | @loggerjs/node/integration-koa |
nestMiddlewareIntegration | @loggerjs/node/integration-nest |
nodeFetchIntegration | @loggerjs/node/integration-fetch |
nodeHttpClientIntegration | @loggerjs/node/integration-http-client |
prismaIntegration | @loggerjs/node/integration-prisma |
queueIntegration | @loggerjs/node/integration-queue |
redisIntegration | @loggerjs/node/integration-redis |
serverlessIntegration | @loggerjs/node/integration-serverless |
diagnosticsChannelIntegration | @loggerjs/node/integration-diagnostics |
installLoggerDiagnosticsChannel | @loggerjs/node/logger-diagnostics |
包导入
根入口在任何环境都可用:
import { createLogger } from "@loggerjs/core";子路径提供更窄的导入:
import { createMiddleware } from "@loggerjs/core/middleware";
import { browserHttpTransport } from "@loggerjs/browser/transport-http";
import { stdoutTransport } from "@loggerjs/node/transport-stdout";每个包都发布 ESM 和 CJS 入口,类型声明按 NodeNext 包解析方式检查。
