# 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 缓冲的浏览器端日志 这些来源定义在: - [backend/app/services/system_logs.py](/home/ray/dev/linkong/planet/backend/app/services/system_logs.py) ### 2. 当前日志读取模型 当前 `read_log_snapshot()` 的职责是: - 读取某个来源的最近若干行 - 解析基础级别与时间 - 按级别、日期、搜索进行过滤 - 返回用于日志页展示的快照 这个模型适合“运维查看器”,但不适合企业级日志系统,原因是: - 读取基于文本尾部扫描,不是基于事件模型 - 不同来源的结构粒度完全不同 - 过滤依赖文本解析,准确率有限 - 没有请求、任务、用户、资源、动作等核心关联字段 ### 3. 已有持久化能力 当前已经存在两个持久化入口: - `record_system_log(...)` - `record_audit_log(...)` 位置: - [backend/app/services/persistent_logs.py](/home/ray/dev/linkong/planet/backend/app/services/persistent_logs.py) 这说明系统并不是从 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 ```json { "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 ```json { "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 ```json { "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 链接 - 推荐动作 - 支持常见纠错动作: - 重试采集任务 - 重载配置 - 跳转到对应模块/资源 - 打开相关日志过滤视图 - 高频错误支持聚合与静默窗口 完成标准: - 已知错误能给出明确建议 - 运维不需要每次都从零猜 ## 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 建议采用: `...` 示例: - `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 才算真正拥有了“企业级日志系统的地基”。