Files
planet/docs/plans/enterprise-logging-system-plan.md
2026-04-24 00:48:33 +08:00

18 KiB
Raw Blame History

Planet 企业级日志系统实施计划

Goal

把 Planet 当前“能看一点运行输出”的日志能力,升级为一套真正可用、可定位、可纠错、可追责、可演进的企业级日志系统。

这里的“企业级”不是指一上来就接入很重的外部平台,而是指这套系统需要同时满足下面五件事:

  1. 排障可用
  2. 历史可查
  3. 业务可解释
  4. 权限操作可追责
  5. 出错后能够反向定位到请求、任务、模块和操作者

最终目标不是“把更多 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.log
  • frontend 读取 /tmp/planet_frontend.log
  • ai-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. 分层,而不是混存

日志必须拆成三层:

  1. 运行日志
  2. 持久化事件日志
  3. 审计日志

它们的用途不同,绝不能继续混成一个概念。

运行日志

用于:

  • 实时排障
  • 观察服务运行状态
  • 看 stdout / stderr / exception / collector 输出

特点:

  • 数据量大
  • 时效性强
  • 保留周期短
  • 不要求每条都落库

持久化事件日志

用于:

  • 记录高价值错误
  • 记录关键业务失败
  • 支撑历史追溯
  • 支撑趋势分析

特点:

  • 只持久化有价值事件
  • 必须结构化
  • 必须有统一 event 命名

审计日志

用于:

  • 留痕
  • 追责
  • 还原高权限操作

特点:

  • 必须单独建模
  • 不与普通运行日志混用

2. 结构化优先

正式日志必须可拆字段,不能长期依赖自由文本。

最低要求至少能拿到:

  • timestamp
  • level
  • service
  • module
  • event
  • message
  • request_id
  • trace_id
  • user_id / actor
  • context

3. 事件命名优先于 message 命名

人看的 message 可以变化,但机器查询和跨模块关联必须依赖稳定事件名。

例如:

  • collector.run.started
  • collector.run.completed
  • collector.run.failed
  • earth.layer.load_failed
  • earth.cruise.route_build_failed
  • system.restart_task.failed
  • auth.websocket.invalid_token

4. 查询链路必须可串联

企业级日志系统的核心不是“有很多日志”,而是“能串起来”。

最终一条高价值事件,至少要能回链到下面任意几类对象:

  • 某个请求
  • 某个任务
  • 某个用户
  • 某个数据源
  • 某个 Earth 模块
  • 某个管理动作

5. 默认脱敏

日志体系必须明确禁止记录:

  • token
  • password
  • Authorization header
  • cookie
  • session
  • 明文敏感个人信息

并且需要有统一脱敏器,而不是靠调用者自觉。

6. “可纠错”不是一句口号

这里的“可纠错”至少包含三层:

  1. 日志字段足够解释错误,方便人排查
  2. 系统能识别常见错误模式并给出纠偏建议
  3. 关键错误支持闭环动作,例如重试、重建索引、重新触发采集、跳转到对应对象

也就是说,这套日志系统最终不只是“告诉你出错了”,而要尽量接近“告诉你为什么出错、怎么修、去哪修”。

Non-Goals

第一阶段不追求:

  • 全量接入 ELK / Loki / Datadog / OpenTelemetry 全家桶
  • 做分布式 trace 全链路可视化大屏
  • 把所有历史日志都迁进数据库
  • 先做特别复杂的规则引擎

第一阶段追求的是:

  • 在当前仓库和当前部署方式下,先把基础日志体系做正确
  • 再为后续平台化接入预留好接口

Target Architecture

推荐目标架构如下。

Layer 1: Runtime Logs

职责:

  • 承载后端、前端开发服务、容器输出、浏览器端缓冲事件
  • 提供最近窗口内的实时查看能力

来源:

  • 文件
  • Docker
  • Redis 缓冲
  • 后续可扩展到 stdout collector

接口:

  • GET /api/v1/system/logs/sources
  • GET /api/v1/system/logs/{source_id}

这层继续保留,但需要做结构化增强和来源补强。

Layer 2: Persistent System Events

职责:

  • 只存高价值事件
  • 供历史追溯、事件列表、趋势和纠错使用

数据来源:

  • 后端关键异常
  • 浏览器端关键失败
  • 采集器/调度器关键失败
  • 业务关键告警与降级事件

接口建议:

  • GET /api/v1/system/events
  • GET /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 聚合

这是“可纠错”能力的关键层。

建议字段:

  • fingerprint
  • root_cause_type
  • known_fix_hint
  • runbook_url
  • related_resource_type
  • related_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
  • 自动注入:
    • service
    • module
    • request_id
    • trace_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 客户端关键错误
    • 数据源不可用
    • 业务降级与恢复
  • 补齐字段:
    • event
    • resource_type
    • resource_id
    • category
    • fingerprint
    • status
  • 增加高频错误去重/聚合策略

完成标准:

  • 高价值错误不再只存在于运行日志里
  • 能查询最近一周/一月的关键失败事件
  • 相同错误具备聚合基础

Phase 3: Frontend And Earth Unified Logger

目标:

  • 把前端从“点状 error 上报”升级成统一前端事件流

工作项:

  • 在前端新增统一 logger API
  • 统一方法:
    • debug
    • info
    • warn
    • error
  • 统一字段:
    • page
    • module
    • event
    • message
    • url
    • user_agent
    • context
  • 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

目标:

  • 把当前“系统日志”页升级为真正的多层日志工作台

工作项:

  • 将页面拆为三个主视图:
    1. 运行日志
    2. 关键事件
    3. 审计日志
  • 运行日志视图:
    • 保留大控制台
    • 支持来源、级别、日期、搜索
  • 关键事件视图:
    • 列表化展示高价值事件
    • 支持聚合、状态、指纹、对象筛选
  • 审计视图:
    • 列表化展示管理员动作
  • 增加详情抽屉:
    • 原始 message
    • context
    • request_id
    • related resource
    • recommended action

完成标准:

  • 日志页不再只是“终端文本窗口”
  • 运维排障、历史追溯、审计留痕三者分层清晰

Phase 6: Corrective Intelligence

目标:

  • 让系统从“能看日志”进化到“能辅助修错”

工作项:

  • 引入错误 fingerprint
  • 对已知错误配置:
    • 根因类型
    • 修复建议
    • runbook 链接
    • 推荐动作
  • 支持常见纠错动作:
    • 重试采集任务
    • 重载配置
    • 跳转到对应模块/资源
    • 打开相关日志过滤视图
  • 高频错误支持聚合与静默窗口

完成标准:

  • 已知错误能给出明确建议
  • 运维不需要每次都从零猜

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.started
  • collector.datasource.run.failed
  • earth.layer.cables.load.failed
  • earth.cruise.route.build.failed
  • system.restart_task.requested
  • system.restart_task.completed
  • auth.websocket.connect.failed
  • settings.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

当下面这些条件成立时,才算这套日志系统真的“成了”:

  1. 一个后端请求失败时,能通过 request_id 在运行日志、持久化事件、审计日志之间串联查询
  2. 一个 Earth 前端错误能定位到页面、模块、事件名和上下文
  3. 一个采集器失败能同时看到运行日志、持久化事件和可执行纠错动作
  4. 一个管理员操作能查到操作者、目标对象、结果和 request_id
  5. 日志页不再只是文本控制台,而是完整的“运行日志 / 关键事件 / 审计日志”工作台
  6. 高频已知错误能聚合并给出修复建议

Delivery Order

推荐严格按下面顺序做,不要乱跳:

  1. Phase 0 命名与字段规范冻结
  2. Phase 1 后端结构化 logging 基础
  3. Phase 2 高价值事件持久化
  4. Phase 3 前端 / Earth 统一 logger
  5. Phase 4 审计覆盖补齐
  6. Phase 5 日志工作台 UI 重构
  7. Phase 6 指纹 / 纠错 / runbook

原因:

  • 如果不先统一字段和命名,后面 UI 和持久化会越来越乱
  • 如果不先做后端结构化基础,前端上报再多也串不起来
  • 如果不先补持久化层,就只有“实时可看”,没有“历史可查”

First Actionable Milestone

如果要从明天就开始做,最合理的第一个里程碑是:

M1: 让后端关键路径全部拥有统一结构化事件

范围:

  • API 请求入口/出口
  • scheduler
  • collectors
  • websocket
  • visualization
  • system control

交付物:

  • 统一 logger helper
  • 统一 event naming 表
  • 统一 request_id 注入
  • 统一脱敏策略
  • 关键模块替换完成

完成这个里程碑后Planet 才算真正拥有了“企业级日志系统的地基”。