Skip to content

迁移说明 ​

本页说明如何从 pino、winston 和 console.log 迁移,并介绍从这些 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()(终端)或 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 迁移 ​

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 logger 代替 winston.loggers 注册表来做按模块的配置;库作者优先使用 core 的 getLogger()。
  • 在顺序基准套件中,LoggerJS lean 路径的吞吐量约为 winston 的 10 倍(见 基准)。

从 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");

每一步带来的收益:日志级别及按级别过滤、用结构化数据代替字符串插值、数据离开进程前先脱敏、批量/离线投递,以及通过错误/进程 integration 采集崩溃路径。


Middleware 与 Processor ​

LoggerJS 有两层同步的数据处理,二者都是一等公民:

  • Middleware 在计算 id、消息或错误之前运行在原始 LogRecord 上。它是补充字段、脱敏或丢弃日志成本最低的位置,并且不会破坏 record 快速路径。
  • Processor 运行在投影后的 LogEvent 上。需要解析后的 event 结构时使用它,例如路由、指纹或 fingers-crossed 缓冲。只要配置了任意 processor,该 logger 就不再走 record 快速路径。

@loggerjs/processors 同时提供两种形式,例如 tagsMiddleware() 和 tagsProcessor()。自定义 middleware 使用 createMiddleware():

ts
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 绑定:

ts
const requestLogger = logger.child({ bindings: { requestId: "req_123" } });

请求作用域使用环境 context:

ts
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。

常见的起步组合:

ts
captureConsoleIntegration({ levels: ["warn", "error"] });
captureBrowserErrorsIntegration();
captureFetchIntegration();
pageLifecycleIntegration();

Transport 与 Codec ​

序列化属于 transport。把 JSON 或字符串格式化从 middleware 和 processor 中移出,交给 transport 的 codec:

ts
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。然后:

  1. 使用 Node 22 或更高版本。 所有包都声明了 engines.node: ">=22.0.0"。
  2. 自行安装 @loggerjs/core。 其他所有包都把它作为 peer dependency,范围是 ^1.0.0,以保证应用中只有一份 core,例如 npm install @loggerjs/core @loggerjs/node。npm 和 pnpm 会自动安装缺失的 peer,Yarn 不会。基于 LoggerJS 构建的库也应把 @loggerjs/core 声明为 peer dependency。
  3. 从子路径导入被移走的导出。 core 根入口只导出内核,browser 和 node 根入口只导出内核加上各自的 stable 组件。下面两节列出了每个被删除导出对应的子路径。
  4. 同时升级所有 @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

包导入 ​

根入口在任何环境都可用:

ts
import { createLogger } from "@loggerjs/core";

子路径提供更窄的导入:

ts
import { createMiddleware } from "@loggerjs/core/middleware";
import { browserHttpTransport } from "@loggerjs/browser/transport-http";
import { stdoutTransport } from "@loggerjs/node/transport-stdout";

每个包都发布 ESM 和 CJS 入口,类型声明按 NodeNext 包解析方式检查。

相关链接 ​

基于 MIT License 发布。