Appearance
📘 日志系统最佳实践
—— CLI、客户端、服务器的三层架构
开发日志 / 架构记录 — 2025
在项目迭代过程中,我逐渐将日志系统整理为 三套互补的方案,分别用于 CLI 工具、客户端(游戏/编辑器)、服务器(生产环境)。
最终形成了 简洁、可维护、可扩展 的三层架构。
本文记录整个设计思路与最终结论,作为后续开发规范参考。
🔷 一、为什么需要三套日志库?
原因很简单:
不同运行环境,对日志的需求完全不同。
| 场景 | 需求 | 不适合 | 合适 |
|---|---|---|---|
| CLI(命令行工具) | 简单、按需启用、调试深度高 | loglevel、winston | debug |
| 客户端(浏览器/游戏/编辑器) | 模块化、多 loggerName、轻量、可自定义前缀 | debug、winston | loglevel |
| 服务器(生产环境) | 文件日志、JSON、数据库、轮转、异常捕获 | debug、loglevel | winston |
最终决定:
- CLI 用 debug
- 客户端用 loglevel
- 服务器用 Winston
这三者组合,正好覆盖所有需求,没有冗余。
🟦 二、CLI 层:使用 debug(按命名空间控制)
CLI 工具通常具备这些特点:
- 运行一次
- 输出量可能很大
- 希望根据模块开关日志
这非常符合 debug 的哲学:
DEBUG=game:* node cli.js
DEBUG=build,core node script.js
DEBUG=*,-express:* node tool.js特点:
- ✔ 零配置
- ✔ 按 namespace 精准控制
- ✔ 关闭时无性能损耗(完全跳过)
- ✔ 输出只到 stdout,不污染其它系统
这是 CLI 最强的选择,没有之一。
🟨 三、客户端:使用 loglevel(浏览器友好、可模块化)
客户端(游戏/工具/编辑器)需要:
- 多模块日志(Game、Net、Editor)
- 动态等级控制(warn、error、info、debug)
- 在 VS Code Debug Console 上保持行号(通过 console 传递)
- 可自定义彩色前缀、美观调试
loglevel 完全适配这些需求。
示例:
ts
const logger = loglevel.getLogger("game");
logger.warn("player disconnected");并可通过 methodFactory 自定义前缀:
[warn] [game] Player lost connection特点:
- ✔ 模块化(每个系统一个 logger)
- ✔ API 简单
- ✔ 可在浏览器、Electron、工具全部复用
- ✔ 不侵入 console 行为(可保持 VS Code 的行号)
这是客户端最优选择。
🟥 四、服务器:使用 Winston(结构化日志 + 文件/数据库)
服务器需要:
- 写入文件(app.log、error.log)
- 文件轮转(按天/按大小自动切分)
- 写入数据库/ElasticSearch
- JSON 格式
- 捕获异常
uncaughtExceptionunhandledRejection
- 多 transport 输出
Winston 做得最成熟:
ts
const logger = winston.createLogger({
level: "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: "logs/app.log" }),
new winston.transports.File({ filename: "logs/error.log", level: "error" })
]
});生产环境日志必须要 可靠可持久化,debug/loglevel 都做不到这一点。
🎯 五、三层结构如何互补?(关键表格总结)
| 层级 | 库 | 理由 |
|---|---|---|
| CLI | debug | namespace 精准开关;关闭时零损耗;命令行体验最好 |
| 客户端 | loglevel | 模块化、可自定义前缀、浏览器友好、可保留 VS Code 行号 |
| 服务器 | winston | 文件/DB 写入、JSON、轮转、生产级能力 |
这三者不重叠,各做各的强项,就是最佳实践。
🧱 六、统一思路(可选):Facade(门面层)
虽然使用了三套日志库,但可以对上层提供统一 API:
ts
log.debug(...)
log.info(...)
log.warn(...)
log.error(...)并在入口注入:
- CLI → debug
- 客户端 → loglevel
- 服务器 → Winston
这样使用上就完全一致。
✅ 七、最终结论
CLI = debug
客户端 = loglevel
服务器 = Winston
这是当前项目中最合理、最轻量、最好维护的日志体系。
