迁移说明
LoggerJS 仍是 pre-1.0,但当前代码库已经从初始骨架走向 v1 架构。本页前半部分说明如何从其他 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()(terminal)或prettyConsoleTransport()(browser DevTools)承担。CoreconsoleTransport()仍是基础本地 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,见 基准);在这个吞吐基础上,你还得到 middleware、integrations、multi-transport fan-out 和同构浏览器故事。
从 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 loggers 替代
winston.loggersregistries 做 per-module configuration;库作者优先使用 core 的getLogger()。 - 当前快照中最快可比路径约为 winston 的 11x(见 基准)。
从 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");每一步获得的收益: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 前运行。
现有代码可以继续使用:
import { redactProcessor } from "@loggerjs/processors";新的 core middleware 可以使用:
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:
const requestLogger = logger.child({ requestId: "req_123" });Request scopes 使用 ambient context:
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。
优先使用:
captureConsoleIntegration({ levels: ["warn", "error"] });
captureBrowserErrorsIntegration();
captureFetchIntegration();
pageLifecycleIntegration();Transports 和 Codecs
序列化属于 transports。把 JSON/stringification 工作从 processors 移到 transport codec:
browserHttpTransport({ url: "/api/logs", codec: safeJsonCodec() });Batch-based transports 现在共享 queue、retry、byte-limit、concurrency 和 circuit-breaker options。
Package Imports
Root package imports 仍然可用:
import { createLogger } from "@loggerjs/core";稳定 subpaths 可用于更窄 imports:
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 检查。