workleap-logging
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWorkleap 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/loggingbash
pnpm add @workleap/loggingCore 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)
日志级别(按严重程度从低到高)
- - Detailed diagnostic info for development
debug - - General application flow events
information - - Non-critical issues needing attention
warning - - Failures preventing functionality
error - - Severe errors risking data integrity
critical
- - 开发阶段的详细诊断信息
debug - - 应用流程的常规事件
information - - 需要关注的非关键问题
warning - - 阻碍功能运行的故障
error - - 可能危及数据完整性的严重错误
critical
Scopes
作用域
Group related log entries under a label. Useful for tracing operations or correlating events.
IMPORTANT: Only a instance can start a scope. If the logger is typed as a (e.g., when using from Squide), you must cast it to before starting a scope:
RootLoggerLoggeruseLogger()RootLoggerts
// Squide example:
import { useLogger } from "@squide/firefly";
import type { RootLogger } from "@workleap/logging";
const logger = useLogger();
(logger as RootLogger).startScope("User signup");将相关日志条目分组到一个标签下,用于追踪操作或关联事件。
重要提示: 只有实例可以启动作用域。如果Logger的类型为(例如使用Squide的时),必须先将其转换为才能启动作用域:
RootLoggerLoggeruseLogger()RootLoggerts
// 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 instance from Workleap libraries standard logging API.
CompositeLoggerts
import { createCompositeLogger, BrowserConsoleLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = createCompositeLogger(false, [new BrowserConsoleLogger(), new LogRocketLogger()]);Parameters:
- : Whether debug information should be logged. If no loggers are provided, creates with a
verboseby default.BrowserConsoleLogger - : Array of loggers to create the
loggerswith.CompositeLogger
工厂函数,用于通过Workleap库标准日志API创建实例。
CompositeLoggerts
import { createCompositeLogger, BrowserConsoleLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"
const logger = createCompositeLogger(false, [new BrowserConsoleLogger(), new LogRocketLogger()]);参数:
- : 是否记录调试信息。如果未提供任何Logger,默认创建一个
verbose。BrowserConsoleLogger - : 用于创建
loggers的Logger数组。CompositeLogger
LogRocket Integration
LogRocket 集成
By default, LogRocket session replays exclude console output. To send log entries to LogRocket, use from or :
LogRocketLogger@workleap/telemetry@workleap/logrocketts
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = new LogRocketLogger();
logger.debug("Application started!");Use to send logs to both browser console and LogRocket:
CompositeLoggerts
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/logrocketLogRocketLoggerts
import { LogRocketLogger } from "@workleap/telemetry"; // 或来自 "@workleap/logrocket"
const logger = new LogRocketLogger();
logger.debug("Application started!");使用同时将日志发送到浏览器控制台和LogRocket:
CompositeLoggerts
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
日志级别指南
| Environment | Recommended Level |
|---|---|
| Development | |
| Production | |
| 环境 | 推荐级别 |
|---|---|
| 开发环境 | |
| 生产环境 | |
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
常见错误
- Forgetting to call log method: Chained segments require ,
.debug(), etc. to output.error() - Not ending scopes: Always call or logs won't output
scope.end() - Using wrong log level: Use for failures, not
errorwarning - Logging sensitive data: Never log passwords, tokens, or PII
- Missing error context: Always include for relevant data and
withObject()for exceptionswithError() - Calling startScope on a non-RootLogger: Only instances can start scopes. When using
RootLoggerfrom Squide, cast touseLogger()first:RootLogger(logger as RootLogger).startScope("Label")
- 忘记调用日志方法:链式片段需要调用、
.debug()等方法才能输出日志.error() - 未结束作用域:必须调用,否则日志不会输出
scope.end() - 日志级别使用错误:故障场景使用而非
errorwarning - 记录敏感数据:绝不能记录密码、令牌或个人可识别信息(PII)
- 缺少错误上下文:始终使用添加相关数据,使用
withObject()捕获异常withError() - 在非RootLogger上调用startScope:只有实例可以启动作用域。使用Squide的
RootLogger时,需先转换类型:useLogger()(logger as RootLogger).startScope("Label")