workleap-logging

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Workleap Logging (@workleap/logging)

Workleap 日志库(@workleap/logging)

A structured logging library for Workleap frontend applications. Provides composable, styled console logging with support for scopes, multiple log levels, and integration with telemetry tools.
为Workleap前端应用打造的结构化日志库。提供可组合、带样式的控制台日志功能,支持作用域、多日志级别,并可与遥测工具集成。

Installation

安装

bash
pnpm add @workleap/logging
bash
pnpm add @workleap/logging

Core Concepts

核心概念

Loggers

Logger

  • BrowserConsoleLogger: Outputs to browser console with styling support
  • CompositeLogger: Forwards logs to multiple underlying loggers
  • BrowserConsoleLogger:输出到浏览器控制台,支持样式定制
  • CompositeLogger:将日志转发至多个底层Logger

Log Levels (lowest to highest severity)

日志级别(按严重程度从低到高)

  1. debug
    - Detailed diagnostic info for development
  2. information
    - General application flow events
  3. warning
    - Non-critical issues needing attention
  4. error
    - Failures preventing functionality
  5. critical
    - Severe errors risking data integrity
  1. debug
    - 开发阶段的详细诊断信息
  2. information
    - 应用流程的常规事件
  3. warning
    - 需要关注的非关键问题
  4. error
    - 阻碍功能运行的故障
  5. critical
    - 可能危及数据完整性的严重错误

Scopes

作用域

Group related log entries under a label. Useful for tracing operations or correlating events.
IMPORTANT: Only a
RootLogger
instance can start a scope. If the logger is typed as a
Logger
(e.g., when using
useLogger()
from Squide), you must cast it to
RootLogger
before starting a scope:
ts
// Squide example:
import { useLogger } from "@squide/firefly";
import type { RootLogger } from "@workleap/logging";

const logger = useLogger();
(logger as RootLogger).startScope("User signup");
将相关日志条目分组到一个标签下,用于追踪操作或关联事件。
重要提示: 只有
RootLogger
实例可以启动作用域。如果Logger的类型为
Logger
(例如使用Squide的
useLogger()
时),必须先将其转换为
RootLogger
才能启动作用域:
ts
// Squide示例:
import { useLogger } from "@squide/firefly";
import type { RootLogger } from "@workleap/logging";

const logger = useLogger();
(logger as RootLogger).startScope("User signup");

API Reference

API 参考

BrowserConsoleLogger

BrowserConsoleLogger

ts
import { BrowserConsoleLogger, LogLevel } from "@workleap/logging";

// Basic usage
const logger = new BrowserConsoleLogger();

// With minimum log level
const logger = new BrowserConsoleLogger({ logLevel: LogLevel.information });
ts
import { BrowserConsoleLogger, LogLevel } from "@workleap/logging";

// 基础用法
const logger = new BrowserConsoleLogger();

// 配置最低日志级别
const logger = new BrowserConsoleLogger({ logLevel: LogLevel.information });

CompositeLogger

CompositeLogger

ts
import { BrowserConsoleLogger, CompositeLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"

const logger = new CompositeLogger([
    new BrowserConsoleLogger(),
    new LogRocketLogger()
]);
ts
import { BrowserConsoleLogger, CompositeLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"

const logger = new CompositeLogger([
    new BrowserConsoleLogger(),
    new LogRocketLogger()
]);

Logger Methods

Logger 方法

Simple logging:
ts
logger.debug("message");
logger.information("message");
logger.warning("message"); // or logger.warn("message")
logger.error("message");
logger.critical("message");
Chained segments (complete chain with log method):
ts
logger
    .withText("Processing order")
    .withObject({ orderId: 123 })
    .withError(new Error("Failed"))
    .error();
Styled text:
ts
logger.withText("Success", {
    style: { color: "green", fontWeight: "bold" }
}).information();
Line breaks:
ts
logger
    .withText("Line 1")
    .withLineChange()
    .withText("Line 2")
    .debug();
简单日志输出:
ts
logger.debug("message");
logger.information("message");
logger.warning("message"); // 或 logger.warn("message")
logger.error("message");
logger.critical("message");
链式片段(需调用日志方法完成输出):
ts
logger
    .withText("Processing order")
    .withObject({ orderId: 123 })
    .withError(new Error("Failed"))
    .error();
带样式的文本:
ts
logger.withText("Success", {
    style: { color: "green", fontWeight: "bold" }
}).information();
换行:
ts
logger
    .withText("Line 1")
    .withLineChange()
    .withText("Line 2")
    .debug();

Scopes

作用域

ts
const scope = logger.startScope("User signup");

scope.information("Form loaded");
scope.debug("Validating...");
scope.withText("Failed").withError(err).error();

// Output all scope entries
scope.end();

// Or dismiss without output
scope.end({ dismiss: true });
Styled scope labels:
ts
// At creation
const scope = logger.startScope("Label", {
    labelStyle: { backgroundColor: "purple", color: "white" }
});

// At end (useful for status-based styling)
scope.end({
    labelStyle: { backgroundColor: "green", color: "white" }
});
ts
const scope = logger.startScope("User signup");

scope.information("Form loaded");
scope.debug("Validating...");
scope.withText("Failed").withError(err).error();

// 输出所有作用域条目
scope.end();

// 或不输出直接关闭
scope.end({ dismiss: true });
带样式的作用域标签:
ts
// 创建时配置
const scope = logger.startScope("Label", {
    labelStyle: { backgroundColor: "purple", color: "white" }
});

// 结束时配置(适合基于状态设置样式)
scope.end({
    labelStyle: { backgroundColor: "green", color: "white" }
});

createCompositeLogger

createCompositeLogger

Factory function to create a
CompositeLogger
instance from Workleap libraries standard logging API.
ts
import { createCompositeLogger, BrowserConsoleLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"

const logger = createCompositeLogger(false, [new BrowserConsoleLogger(), new LogRocketLogger()]);
Parameters:
  • verbose
    : Whether debug information should be logged. If no loggers are provided, creates with a
    BrowserConsoleLogger
    by default.
  • loggers
    : Array of loggers to create the
    CompositeLogger
    with.
工厂函数,用于通过Workleap库标准日志API创建
CompositeLogger
实例。
ts
import { createCompositeLogger, BrowserConsoleLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"

const logger = createCompositeLogger(false, [new BrowserConsoleLogger(), new LogRocketLogger()]);
参数:
  • verbose
    : 是否记录调试信息。如果未提供任何Logger,默认创建一个
    BrowserConsoleLogger
  • loggers
    : 用于创建
    CompositeLogger
    的Logger数组。

LogRocket Integration

LogRocket 集成

By default, LogRocket session replays exclude console output. To send log entries to LogRocket, use
LogRocketLogger
from
@workleap/telemetry
or
@workleap/logrocket
:
ts
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"

const logger = new LogRocketLogger();
logger.debug("Application started!");
Use
CompositeLogger
to send logs to both browser console and LogRocket:
ts
import { BrowserConsoleLogger, CompositeLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"

const logger = new CompositeLogger([
    new BrowserConsoleLogger(),
    new LogRocketLogger()
]);

logger.debug("Application started!"); // Processed by both loggers
默认情况下,LogRocket会话回放会排除控制台输出。要将日志条目发送到LogRocket,请使用
@workleap/telemetry
@workleap/logrocket
中的
LogRocketLogger
ts
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"

const logger = new LogRocketLogger();
logger.debug("Application started!");
使用
CompositeLogger
同时将日志发送到浏览器控制台和LogRocket:
ts
import { BrowserConsoleLogger, CompositeLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"

const logger = new CompositeLogger([
    new BrowserConsoleLogger(),
    new LogRocketLogger()
]);

logger.debug("Application started!"); // 由两个Logger共同处理

Common Patterns

常见模式

Application logger setup

应用Logger配置

ts
import { BrowserConsoleLogger, CompositeLogger, LogLevel } from "@workleap/logging";

const isDev = process.env.NODE_ENV === "development";

const logger = new BrowserConsoleLogger({
    logLevel: isDev ? LogLevel.debug : LogLevel.information
});
ts
import { BrowserConsoleLogger, CompositeLogger, LogLevel } from "@workleap/logging";

const isDev = process.env.NODE_ENV === "development";

const logger = new BrowserConsoleLogger({
    logLevel: isDev ? LogLevel.debug : LogLevel.information
});

Error logging

错误日志

ts
try {
    await processOrder(orderId);
} catch (error) {
    logger
        .withText("Failed to process order")
        .withObject({ orderId })
        .withError(error as Error)
        .error();
}
ts
try {
    await processOrder(orderId);
} catch (error) {
    logger
        .withText("Failed to process order")
        .withObject({ orderId })
        .withError(error as Error)
        .error();
}

Feature/operation scoping

功能/操作作用域

ts
async function registerModule(moduleName: string) {
    const scope = logger.startScope(`${moduleName} registration`);

    try {
        scope.debug("Registering routes...");
        await registerRoutes();
        scope.debug("Routes registered");

        scope.debug("Fetching data...");
        await fetchData();
        scope.debug("Data loaded");

        scope.end({ labelStyle: { color: "green" } });
    } catch (error) {
        scope.withText("Registration failed").withError(error as Error).error();
        scope.end({ labelStyle: { color: "red" } });
        throw error;
    }
}
ts
async function registerModule(moduleName: string) {
    const scope = logger.startScope(`${moduleName} registration`);

    try {
        scope.debug("Registering routes...");
        await registerRoutes();
        scope.debug("Routes registered");

        scope.debug("Fetching data...");
        await fetchData();
        scope.debug("Data loaded");

        scope.end({ labelStyle: { color: "green" } });
    } catch (error) {
        scope.withText("Registration failed").withError(error as Error).error();
        scope.end({ labelStyle: { color: "red" } });
        throw error;
    }
}

Multi-destination logging

多目标日志

ts
import { BrowserConsoleLogger, CompositeLogger, LogLevel } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"

const logger = new CompositeLogger([
    new BrowserConsoleLogger({
        logLevel: LogLevel.error
    }),
    new LogRocketLogger({
        logLevel: LogLevel.debug
    })
]);
ts
import { BrowserConsoleLogger, CompositeLogger, LogLevel } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"

const logger = new CompositeLogger([
    new BrowserConsoleLogger({
        logLevel: LogLevel.error
    }),
    new LogRocketLogger({
        logLevel: LogLevel.debug
    })
]);

Log Level Guidelines

日志级别指南

EnvironmentRecommended Level
Development
debug
Production
information
环境推荐级别
开发环境
debug
生产环境
information

PR Review Checklist

PR 审核清单

When reviewing logging changes:
  • Verify appropriate log levels (debug for diagnostics, error for failures)
  • Check that errors include context (withObject) and stack traces (withError)
  • Ensure scopes are properly ended (end() or end({ dismiss: true }))
  • Confirm no sensitive data in log messages
  • Verify CompositeLogger filters are set per environment
审核日志相关变更时:
  • 验证日志级别是否合适(debug用于诊断,error用于故障)
  • 检查错误日志是否包含上下文(withObject)和堆栈跟踪(withError)
  • 确保作用域已正确结束(调用end()或end({ dismiss: true }))
  • 确认日志消息中无敏感数据
  • 验证CompositeLogger的过滤规则是否按环境配置

Common Mistakes

常见错误

  1. Forgetting to call log method: Chained segments require
    .debug()
    ,
    .error()
    , etc. to output
  2. Not ending scopes: Always call
    scope.end()
    or logs won't output
  3. Using wrong log level: Use
    error
    for failures, not
    warning
  4. Logging sensitive data: Never log passwords, tokens, or PII
  5. Missing error context: Always include
    withObject()
    for relevant data and
    withError()
    for exceptions
  6. Calling startScope on a non-RootLogger: Only
    RootLogger
    instances can start scopes. When using
    useLogger()
    from Squide, cast to
    RootLogger
    first:
    (logger as RootLogger).startScope("Label")
  1. 忘记调用日志方法:链式片段需要调用
    .debug()
    .error()
    等方法才能输出日志
  2. 未结束作用域:必须调用
    scope.end()
    ,否则日志不会输出
  3. 日志级别使用错误:故障场景使用
    error
    而非
    warning
  4. 记录敏感数据:绝不能记录密码、令牌或个人可识别信息(PII)
  5. 缺少错误上下文:始终使用
    withObject()
    添加相关数据,使用
    withError()
    捕获异常
  6. 在非RootLogger上调用startScope:只有
    RootLogger
    实例可以启动作用域。使用Squide的
    useLogger()
    时,需先转换类型:
    (logger as RootLogger).startScope("Label")