18 KiB
Planet 企业级日志系统实施计划
Goal
把 Planet 当前“能看一点运行输出”的日志能力,升级为一套真正可用、可定位、可纠错、可追责、可演进的企业级日志系统。
这里的“企业级”不是指一上来就接入很重的外部平台,而是指这套系统需要同时满足下面五件事:
- 排障可用
- 历史可查
- 业务可解释
- 权限操作可追责
- 出错后能够反向定位到请求、任务、模块和操作者
最终目标不是“把更多 stdout 放到日志页里”,而是建立一套统一的日志契约与落地链路:
- 统一日志字段
- 统一事件命名
- 统一采集入口
- 统一查询视图
- 清晰的实时日志、持久化事件、审计日志分层
Why
当前仓库已经有一些日志基础,但离真正可用的日志系统还有明显距离。
已有基础:
- 后端运行日志可通过
/tmp/planet_backend.log查看 - 前端开发服务日志可通过
/tmp/planet_frontend.log查看 - AI Provider 可从 Docker 容器读取日志
- Earth 浏览器端关键日志可上报到后端并进入 Redis 缓冲
- 已有
system_logs/audit_logs持久化能力 - 管理台已有“系统日志”页面,支持来源、级别、日期、搜索
当前缺口:
- 后端日志仍以
uvicorn/ 文本输出为主,不是统一结构化事件流 - 不同模块的日志格式不一致,很多地方只有 message,没有 event 语义
- 还没有统一的后端 logger 封装与字段注入机制
- 前端虽然能上报错误,但还没有统一 logger API 和统一事件词汇
- Earth 与管理台之间的错误事件还没有形成可串联的事件链路
- 历史持久化还偏点状,很多高价值失败并没有系统性落库
- 系统日志页当前更像“运行输出查看器”,不是“多层日志查询台”
- 审计日志与运行日志尚未形成明确的产品级联动
所以当前真正的问题不是“有没有日志页”,而是:
当前系统能看见输出,但还不能稳定回答“发生了什么、影响了谁、在哪条链路上坏了、是否已修复、是谁触发的”。
Current State
截至 2026-04-23,当前代码中的日志相关能力大致如下。
1. 日志来源
当前系统日志页主要读取以下来源:
backend读取/tmp/planet_backend.logfrontend读取/tmp/planet_frontend.logai-provider读取 Docker 容器日志earth-client读取 Redis 缓冲的浏览器端日志
这些来源定义在:
2. 当前日志读取模型
当前 read_log_snapshot() 的职责是:
- 读取某个来源的最近若干行
- 解析基础级别与时间
- 按级别、日期、搜索进行过滤
- 返回用于日志页展示的快照
这个模型适合“运维查看器”,但不适合企业级日志系统,原因是:
- 读取基于文本尾部扫描,不是基于事件模型
- 不同来源的结构粒度完全不同
- 过滤依赖文本解析,准确率有限
- 没有请求、任务、用户、资源、动作等核心关联字段
3. 已有持久化能力
当前已经存在两个持久化入口:
record_system_log(...)record_audit_log(...)
位置:
这说明系统并不是从 0 开始,但也说明当前最大的问题是:
持久化能力存在,但没有成为统一默认路径。
4. 已有 request_id 基础
当前系统已具备 request_id 相关基础,部分持久化能力也会尝试写入 request_id。
这为后续做:
- 请求链路排障
- 前后端关联查询
- 任务执行追踪
提供了很好的基础。
5. 当前日志页定位
当前日志页已经具备:
- 来源切换
- 级别筛选
- 日期筛选
- 搜索
- 文本控制台视图
但它仍然是“单层视图”:
- 上面是筛选器
- 下面是一块文本控制台
它还不是:
- 运行日志 + 事件日志 + 审计日志 的统一入口
- 也没有事件详情、关联跳转、纠错建议、链路追踪能力
Core Principles
这套日志系统后续必须遵循下面几个原则。
1. 分层,而不是混存
日志必须拆成三层:
- 运行日志
- 持久化事件日志
- 审计日志
它们的用途不同,绝不能继续混成一个概念。
运行日志
用于:
- 实时排障
- 观察服务运行状态
- 看 stdout / stderr / exception / collector 输出
特点:
- 数据量大
- 时效性强
- 保留周期短
- 不要求每条都落库
持久化事件日志
用于:
- 记录高价值错误
- 记录关键业务失败
- 支撑历史追溯
- 支撑趋势分析
特点:
- 只持久化有价值事件
- 必须结构化
- 必须有统一 event 命名
审计日志
用于:
- 留痕
- 追责
- 还原高权限操作
特点:
- 必须单独建模
- 不与普通运行日志混用
2. 结构化优先
正式日志必须可拆字段,不能长期依赖自由文本。
最低要求至少能拿到:
timestamplevelservicemoduleeventmessagerequest_idtrace_iduser_id/actorcontext
3. 事件命名优先于 message 命名
人看的 message 可以变化,但机器查询和跨模块关联必须依赖稳定事件名。
例如:
collector.run.startedcollector.run.completedcollector.run.failedearth.layer.load_failedearth.cruise.route_build_failedsystem.restart_task.failedauth.websocket.invalid_token
4. 查询链路必须可串联
企业级日志系统的核心不是“有很多日志”,而是“能串起来”。
最终一条高价值事件,至少要能回链到下面任意几类对象:
- 某个请求
- 某个任务
- 某个用户
- 某个数据源
- 某个 Earth 模块
- 某个管理动作
5. 默认脱敏
日志体系必须明确禁止记录:
- token
- password
- Authorization header
- cookie
- session
- 明文敏感个人信息
并且需要有统一脱敏器,而不是靠调用者自觉。
6. “可纠错”不是一句口号
这里的“可纠错”至少包含三层:
- 日志字段足够解释错误,方便人排查
- 系统能识别常见错误模式并给出纠偏建议
- 关键错误支持闭环动作,例如重试、重建索引、重新触发采集、跳转到对应对象
也就是说,这套日志系统最终不只是“告诉你出错了”,而要尽量接近“告诉你为什么出错、怎么修、去哪修”。
Non-Goals
第一阶段不追求:
- 全量接入 ELK / Loki / Datadog / OpenTelemetry 全家桶
- 做分布式 trace 全链路可视化大屏
- 把所有历史日志都迁进数据库
- 先做特别复杂的规则引擎
第一阶段追求的是:
- 在当前仓库和当前部署方式下,先把基础日志体系做正确
- 再为后续平台化接入预留好接口
Target Architecture
推荐目标架构如下。
Layer 1: Runtime Logs
职责:
- 承载后端、前端开发服务、容器输出、浏览器端缓冲事件
- 提供最近窗口内的实时查看能力
来源:
- 文件
- Docker
- Redis 缓冲
- 后续可扩展到 stdout collector
接口:
GET /api/v1/system/logs/sourcesGET /api/v1/system/logs/{source_id}
这层继续保留,但需要做结构化增强和来源补强。
Layer 2: Persistent System Events
职责:
- 只存高价值事件
- 供历史追溯、事件列表、趋势和纠错使用
数据来源:
- 后端关键异常
- 浏览器端关键失败
- 采集器/调度器关键失败
- 业务关键告警与降级事件
接口建议:
GET /api/v1/system/eventsGET /api/v1/system/events/{id}POST /api/v1/system/events/{id}/actions/...(后续)
Layer 3: Audit Logs
职责:
- 留痕高权限操作
- 记录操作者、对象、结果、请求号
接口建议:
GET /api/v1/system/audit-logs
Layer 4: Error Intelligence / Triage
职责:
- 对高频错误做归类
- 对已知错误给出解释与建议动作
- 对相同错误进行 fingerprint 聚合
这是“可纠错”能力的关键层。
建议字段:
fingerprintroot_cause_typeknown_fix_hintrunbook_urlrelated_resource_typerelated_resource_id
Canonical Event Model
推荐统一事件字段模型如下。
Runtime Log Record
{
"timestamp": "2026-04-23T10:15:30Z",
"level": "error",
"service": "backend",
"module": "app.services.scheduler",
"event": "collector.run.failed",
"message": "Collector bgp_news failed",
"request_id": "req_xxx",
"trace_id": "trace_xxx",
"user_id": null,
"actor": null,
"resource_type": "collector",
"resource_id": "bgp_news",
"context": {
"datasource_id": 12,
"exception_type": "TimeoutError"
}
}
Persistent System Event
{
"id": 1024,
"event": "earth.layer.load_failed",
"level": "error",
"source": "earth-client",
"service": "earth",
"module": "cables",
"message": "Failed to load cable layer",
"fingerprint": "earth.layer.load_failed:cables:network_timeout",
"request_id": "req_xxx",
"trace_id": null,
"user_id": 1,
"resource_type": "earth_layer",
"resource_id": "cables",
"category": "visualization",
"status": "open",
"context": {
"url": "/api/v1/visualization/geo/cables"
},
"created_at": "2026-04-23T10:15:30Z"
}
Audit Log
{
"id": 88,
"action": "system.restart_task.requested",
"actor_id": 1,
"actor_name": "root",
"target_type": "restart_task",
"target_id": "restart_20260423_xxx",
"result": "success",
"request_id": "req_xxx",
"ip": "127.0.0.1",
"details": {
"action": "restart_backend"
},
"created_at": "2026-04-23T10:15:30Z"
}
Implementation Plan
Phase 0: Logging Inventory And Naming Freeze
目标:
- 先统一“记录什么”和“怎么命名”,避免后面越做越乱
工作项:
- 盘点当前所有
logging.getLogger使用点 - 盘点裸
print - 盘点
record_system_log/record_audit_log已落点位 - 建立统一事件命名表
- 定义 service / module / category / resource 字段枚举
- 输出日志字段白名单和脱敏规范
完成标准:
- 有一份稳定的事件命名清单
- 有一份字段规范清单
- 后续新增日志不再“临时起名”
Phase 1: Backend Structured Logging Foundation
目标:
- 把后端从“散落 logging + 文本输出”升级成“统一结构化 logger”
工作项:
- 新增统一后端 logger helper,例如
app/core/logging.py - 自动注入:
servicemodulerequest_idtrace_id
- 增加统一脱敏 filter
- 把关键模块先切到统一 logger:
- API 层
- scheduler
- collectors
- websocket
- visualization
- system control
- 约束:
- 正式路径禁止裸
print - 正式异常优先
logger.exception(..., extra={...})
- 正式路径禁止裸
完成标准:
- 后端关键模块都有稳定
event - request 日志和异常日志能挂上
request_id - 不再依赖只看
uvicorn原生文本输出来定位问题
Phase 2: Persistent Event Layer
目标:
- 把“值得长期保留的错误和关键事件”系统性落库
工作项:
- 重新定义
record_system_log()的使用边界 - 明确哪些事件必须持久化:
- API 关键失败
- 调度器失败
- 采集器失败
- Earth 客户端关键错误
- 数据源不可用
- 业务降级与恢复
- 补齐字段:
eventresource_typeresource_idcategoryfingerprintstatus
- 增加高频错误去重/聚合策略
完成标准:
- 高价值错误不再只存在于运行日志里
- 能查询最近一周/一月的关键失败事件
- 相同错误具备聚合基础
Phase 3: Frontend And Earth Unified Logger
目标:
- 把前端从“点状 error 上报”升级成统一前端事件流
工作项:
- 在前端新增统一 logger API
- 统一方法:
debuginfowarnerror
- 统一字段:
pagemoduleeventmessageurluser_agentcontext
- Earth 模块优先接入:
- layer load failed
- cruise build failed
- popup render failed
- connector render failed
- websocket dropped
- 管理台优先接入:
- settings save failed
- datasource toggle failed
- restart task submit failed
完成标准:
- 前端日志事件名与后端可对齐
- Earth 和管理台关键失败不再只停留在 console
- 浏览器端关键问题能进入统一系统日志/事件层
Phase 4: Audit Logging Completion
目标:
- 把管理员与高权限操作真正做成企业级审计
工作项:
- 扩大审计覆盖面:
- 系统重启
- 数据源启停
- 调度规则变更
- 配置变更
- 人工触发采集
- 删除/修改关键配置
- 增加字段:
- actor
- target
- before / after
- request_id
- IP
- 审计页支持:
- 动作筛选
- 操作者筛选
- 时间筛选
- 目标对象筛选
完成标准:
- 所有高权限操作都能追到人、时间、对象、结果
Phase 5: Log Console To Enterprise Observability UI
目标:
- 把当前“系统日志”页升级为真正的多层日志工作台
工作项:
- 将页面拆为三个主视图:
- 运行日志
- 关键事件
- 审计日志
- 运行日志视图:
- 保留大控制台
- 支持来源、级别、日期、搜索
- 关键事件视图:
- 列表化展示高价值事件
- 支持聚合、状态、指纹、对象筛选
- 审计视图:
- 列表化展示管理员动作
- 增加详情抽屉:
- 原始 message
- context
- request_id
- related resource
- recommended action
完成标准:
- 日志页不再只是“终端文本窗口”
- 运维排障、历史追溯、审计留痕三者分层清晰
Phase 6: Corrective Intelligence
目标:
- 让系统从“能看日志”进化到“能辅助修错”
工作项:
- 引入错误 fingerprint
- 对已知错误配置:
- 根因类型
- 修复建议
- runbook 链接
- 推荐动作
- 支持常见纠错动作:
- 重试采集任务
- 重载配置
- 跳转到对应模块/资源
- 打开相关日志过滤视图
- 高频错误支持聚合与静默窗口
完成标准:
- 已知错误能给出明确建议
- 运维不需要每次都从零猜
Recommended Module Changes
Backend
建议新增/增强的模块:
backend/app/core/logging.py- 统一 logger 封装
- formatter
- filter
- request/trace 注入
backend/app/services/persistent_logs.py- 扩展字段
- 统一持久化策略
backend/app/services/system_logs.py- 逐步从“文本尾部查看器”升级为“运行日志聚合器”
backend/app/services/log_classification.py- 指纹
- 根因分类
- 纠错建议
backend/app/api/v1/system_control.py- 补充事件 / 审计 / 日志多视图接口
Frontend
建议新增/增强:
frontend/src/lib/logger.ts- 统一前端 logger API
frontend/src/pages/Logs/Logs.tsx- 升级为多层工作台
frontend/public/earth/js/...- 各 Earth 模块接入统一事件 logger
Event Naming Convention
建议采用:
<domain>.<resource>.<action>.<result>
示例:
collector.datasource.run.startedcollector.datasource.run.failedearth.layer.cables.load.failedearth.cruise.route.build.failedsystem.restart_task.requestedsystem.restart_task.completedauth.websocket.connect.failedsettings.datasource.priority.updated
规则:
- 不用自然语言句子
- 不把 ID 塞进 event 名里
- 资源对象通过字段承载,不通过 event 名承载
Query Model
最终推荐支持的查询维度:
- 时间范围
- level
- source
- service
- module
- event
- request_id
- trace_id
- user_id / actor
- resource_type / resource_id
- category
- fingerprint
- status
- full-text search
Retention Strategy
推荐保留策略:
- 运行日志:
- 文件 / 容器 / Redis 缓冲保留短周期
- 持久化事件:
- 保留中长期
- 审计日志:
- 长期保留
初版可以先这样:
- 运行日志:7 到 14 天
- 关键事件:90 到 180 天
- 审计日志:180 天以上
后续再根据存储与合规要求调整。
Security And Compliance
必须落实:
- 敏感字段脱敏
- 前端上报白名单
- 防止日志注入
- 审计日志不可被普通管理员随意篡改
- 高敏感纠错动作必须再次鉴权
Success Criteria
当下面这些条件成立时,才算这套日志系统真的“成了”:
- 一个后端请求失败时,能通过
request_id在运行日志、持久化事件、审计日志之间串联查询 - 一个 Earth 前端错误能定位到页面、模块、事件名和上下文
- 一个采集器失败能同时看到运行日志、持久化事件和可执行纠错动作
- 一个管理员操作能查到操作者、目标对象、结果和 request_id
- 日志页不再只是文本控制台,而是完整的“运行日志 / 关键事件 / 审计日志”工作台
- 高频已知错误能聚合并给出修复建议
Delivery Order
推荐严格按下面顺序做,不要乱跳:
- Phase 0 命名与字段规范冻结
- Phase 1 后端结构化 logging 基础
- Phase 2 高价值事件持久化
- Phase 3 前端 / Earth 统一 logger
- Phase 4 审计覆盖补齐
- Phase 5 日志工作台 UI 重构
- Phase 6 指纹 / 纠错 / runbook
原因:
- 如果不先统一字段和命名,后面 UI 和持久化会越来越乱
- 如果不先做后端结构化基础,前端上报再多也串不起来
- 如果不先补持久化层,就只有“实时可看”,没有“历史可查”
First Actionable Milestone
如果要从明天就开始做,最合理的第一个里程碑是:
M1: 让后端关键路径全部拥有统一结构化事件
范围:
- API 请求入口/出口
- scheduler
- collectors
- websocket
- visualization
- system control
交付物:
- 统一 logger helper
- 统一 event naming 表
- 统一 request_id 注入
- 统一脱敏策略
- 关键模块替换完成
完成这个里程碑后,Planet 才算真正拥有了“企业级日志系统的地基”。