跳转到内容

Logger

Logger 组装平台无关的日志记录,执行限流和大小保护,并交给显式注入的 transport。它不会自动选择客户端、控制台或网络上报。

创建与发送

ts
import { createLogger, type LoggerTransport } from '@freenotes/web-runtime';

const transport: LoggerTransport = async (record) => {
  await sendToLogService(record);
};

const logger = createLogger({
  source: 'checkout',
  transport,
  sessionId: 'session-1',
  getRoute: () => location.pathname,
  getTraceId: () => currentTraceId
});

await logger.info('page_view', { orderId: '1' });
await logger.warning('request_retry', { retryCount: 1 });
await logger.error('request_failed', {
  error: new Error('timeout'),
  context: { orderId: '1' }
});

source 是非空业务标识,transport 必填;无效时创建阶段抛 TypeError。配置在创建时捕获,之后修改原配置对象不会改变已有实例。sessionId 在实例创建时确定,未提供则生成;getRoutegetTraceId 每条日志读取,getter 异常会被忽略。

支持 verbosedebuginfowarningerror。Logger 不自动代理 console,不注册全局异常处理,不绑定框架或页面生命周期。

结果与失败

所有日志方法返回 Promise<LoggerResult>

status含义
dispatchedtransport 调用完成。客户端 transport 只确认调用已发出,不保证 Native 已落盘。
skipped日志被跳过,reason 为 disabledrate-limited
failed无效记录或 transport 失败,error 保留异常。

transport 返回 void / Promise<void>,失败通过 throw / reject 表达。Logger 将失败转换为结果;可通过 onTransportError(error) 观察 transport 异常,诊断回调本身的异常不会影响业务。

类型化上下文

ts
const logger = createLogger<{ orderId: string }>({
  source: 'checkout',
  transport(record) {
    record.context?.orderId; // string | undefined
    record.error?.message; // string | undefined
  }
});
await logger.error('submit_failed', {
  error: new Error('failed'),
  context: { orderId: '1' }
});

error(event, { error, context }) 显式区分异常与上下文。error 接受 unknown;Error 保留名称、消息和堆栈,其他抛出值归一化为 name: 'NonError' 的文本信息。省略 error 时只记录 context。

record.context 只表示业务上下文。错误名称、消息、堆栈存放在独立的 record.error,不覆盖业务的同名属性。error.message 同样受 maxMessageLength 限制;error.name 最多保留 128 个 UTF-16 code unit,error.stack 最多保留 8192 个,不修改原始异常。

循环引用或过大的 context 会被省略,并设置 contextTruncated: 'context-size-limit'。不会用一个宽泛的对象替换声明的 context 类型。

Bridge 日志 transport 预设

ts
import { createBridgeLogTransport, createLogger } from '@freenotes/web-runtime';

const logger = createLogger({
  source: 'checkout',
  transport: createBridgeLogTransport({ maxPayloadBytes: 16 * 1024 })
});

该预设对应现有 App 的 writeClientLog / h5_client_log 协议;其他协议通过自定义 LoggerTransport 接入。配置在创建时捕获。显式启用后,transport 在发送时检测宿主并延迟加载 dsBridge,使用 writeClientLog 方法与 h5_client_log tag。sessionId 映射为 wire message 的 h5SessionId;错误和截断标识转换为客户端 context 协议。

自定义宿主可传 createBridgeLogTransport({ call }),call 使用 DSBridge 的 (method, payload, callback) 签名。客户端不可用或 payload 无法满足限制时,transport 抛错,经 Logger 转为 failedmaxPayloadBytes 只配置在客户端 transport 上,不影响其他发送通道。超限时省略业务 context、清空顶层 message、移除堆栈,并保留错误名称及消息摘要;摘要按包含 JSON 转义的实际 UTF-8 字节数继续缩短。若最小记录仍超限则返回 failed。

限流和大小保护

ts
const logger = createLogger({
  source: 'checkout',
  transport,
  maxContextBytes: 8 * 1024,
  maxMessageLength: 2048,
  rateLimit: { windowMs: 1000, maxCount: 30, perEventMaxCount: 5 }
});

默认启用全局、级别和单事件限流。rateLimit: false 关闭限流,enabled: false 跳过发送。maxContextBytes 按 UTF-8 字节数衡量,maxMessageLength 限制 message 长度。

让基础能力保持简单,让业务开发更加专注。