LogDisplayer / CLAUDE.md
Beracles's picture
新增 CLAUDE.md 文档并优化日志管理的缓存机制
83705b6

CLAUDE.md

此文件为 Claude Code (claude.ai/code) 在此存储库中工作时提供指导。

项目概述

LogDisplayer 是一个基于 FastAPI 的日志聚合和显示系统,可以从多个端点/源收集日志,将其存储在本地,并同步到 Hugging Face 数据集。它提供了一个 Web UI,用于查看和管理带有 JWT 令牌用户认证的日志。

技术栈:

  • 后端:FastAPI + Uvicorn(Python 3.10+)
  • 数据存储:Hugging Face Datasets、Pandas
  • 云同步:Hugging Face Hub API
  • 后台任务:APScheduler
  • 前端:Jinja2 模板(HTML/CSS/JavaScript)
  • 部署:Docker

开发设置与命令

前置要求

  • Python 3.10+
  • pip 包管理器
  • 环境变量:hf_token(Hugging Face 令牌)、SECRET_KEY(用于 JWT 解析)

安装依赖

pip install -r requirements.txt

运行应用程序

# 标准开发运行
uvicorn main:app --host 0.0.0.0 --port 7860

# 带自动重载的开发运行
uvicorn main:app --reload --host 0.0.0.0 --port 7860

应用将在 http://localhost:7860 可用

Docker 开发

# 构建 Docker 镜像
docker build -t log-displayer .

# 运行 Docker 容器
docker run -p 7860:7860 \
  -e hf_token="your_hf_token" \
  -e SECRET_KEY="your_secret_key" \
  log-displayer

测试

当前没有配置正式的测试框架。手动测试脚本位于 scratch/

  • scratch/test_dataset_to_dict.py - 测试数据集转换
  • scratch/test_glob.py - 测试文件搜索

运行手动测试:

python scratch/test_dataset_to_dict.py
python scratch/test_glob.py

架构概览

核心组件

1. main.py(FastAPI 应用)

  • 初始化 FastAPI 应用,配置 CORS 中间件
  • 定义 3 个主要端点:
    • POST /{end} - 接受日志,包含消息体、可选的令牌头和源头
    • GET /healthcheck - 健康检查端点
    • GET /GET "" - 使用所有日志渲染 HTML 模板
  • 实例化和管理 LoggingHelper 实例

2. logging_helper.py(日志管理引擎)

  • LoggingHelper 类处理所有日志持久化和同步
  • 关键方法:
    • addlog(log) - 将日志添加到内存缓冲区
    • pull() - 从 Hugging Face 下载今天的日志
    • push() - 将缓冲的日志上传到 Hugging Face 数据集(标记缓存需要刷新)
    • push_yesterday() - 归档昨天的日志
    • refresh() - [优化] 返回所有日志作为排序的字典列表,使用 DataFrame 缓存机制避免重复加载
    • _load_all_logs() - [新增] 从磁盘加载所有日志文件并合并成 DataFrame
  • 后台同步: 使用 APScheduler 定期推送日志(默认:60 秒间隔)
  • 文件组织: 日志在 HF 中组织为 {year}/{month}/{day}/*.json
  • 缓冲策略: 内存中的 Hugging Face 数据集字典,按文件路径和需要推送状态跟踪
  • 缓存策略: DataFrame 缓存 + 智能失效。只在 push() 完成或首次加载时重新读取磁盘文件

3. utils.py(辅助函数)

  • beijing() - 返回 Asia/Shanghai 时区的当前时间
  • parse_token(token) - 解码 JWT 令牌以提取 uid 和用户名
  • decode_jwt(token) - 使用 SECRET_KEY 解码 JWT
  • md5(text) - 生成 MD5 哈希(用于日志文件名)
  • json_to_str(obj) - 将 JSON 转换为紧凑字符串格式

4. static/index.html(前端模板)

  • 带有中文 UI 的 Jinja2 模板
  • 显示带有排序和过滤的日志表格
  • 显示列:类型、来源、用户、时间戳、内容

数据流

日志 POST 请求
  → main.py add_log()
  → parse_token() 获取用户信息
  → logging_helper.addlog()(添加到缓冲区)
  → APScheduler 每 60 秒触发 push()
  → logging_helper.push()(保存到本地 JSON,上传到 HF)
  → 设置 cache_needs_refresh = True

日志显示请求(带缓存优化)
  → GET / 或 GET ""
  → logging_helper.refresh()
  → 调用 push()(如无新日志,快速返回)
  → 检查缓存:
     - 如果 cache_needs_refresh == True 或缓存为空 → _load_all_logs()(从磁盘加载)
     - 否则 → 直接返回缓存的 DataFrame
  → 返回排序的字典列表
  → Jinja2 渲染 HTML 模板

环境变量

必需:

  • hf_token - Hugging Face API 令牌,用于认证
  • SECRET_KEY - 用于 JWT 解码的密钥(用于解析用户令牌)

关键设计模式

  1. 两级缓冲: 内存缓冲 + 磁盘存储。日志在 Python 对象中缓冲,定期写入 JSON,然后推送到 Hugging Face。
  2. 基于日期的组织: 日志自动组织到年/月/日目录中,便于归档数据管理。
  3. 后台同步: APScheduler 确保定期推送日志,而不会阻止主请求处理程序。
  4. 无状态端点: 每个请求都是独立的;用户信息在每次调用时从 JWT 令牌中提取。
  5. DataFrame 缓存(性能优化): refresh() 方法缓存合并后的 DataFrame。只有在 push() 完成后才重新加载磁盘文件,避免每次刷新都重复读取和解析所有 JSON 文件。

重要文件与职责

文件 行数 用途
main.py 74 FastAPI 应用初始化、端点定义
logging_helper.py 235 核心日志持久化、缓冲、HF 同步和缓存机制
utils.py 64 时区、JWT 解析、哈希工具函数
static/index.html ~400 Jinja2 Web UI 模板
requirements.txt 10 Python 依赖
Dockerfile - Docker 镜像定义
data/logs/ - 本地日志文件存储

性能优化说明

首页刷新优化(v1.1)

问题: 之前每次刷新首页都需要从磁盘重新加载所有 JSON 日志文件,在日志数量较多时会导致加载时间过长。

解决方案: 实现了 DataFrame 缓存机制。

具体改进:

  1. DataFrame 内存缓存 - 在 LoggingHelper 中添加 cached_df 变量存储合并后的 DataFrame
  2. 智能缓存失效 - 只有在调用 push() 方法写入新日志到磁盘后,才设置 cache_needs_refresh = True 标记
  3. 增量加载 - 新增 _load_all_logs() 私有方法,只在必要时(首次加载或 push 完成后)从磁盘重新加载数据

性能改进:

  • 首次刷新: 需要加载所有 JSON 文件(不可避免)
  • 后续刷新(无新日志): 直接返回缓存,避免磁盘 I/O,响应时间从秒级降低到毫秒级
  • 后续刷新(有新日志): push() 完成后重新加载,但由于 push() 已经处理完新日志,只需一次加载即可

相关代码变更:

常见开发任务

添加新的日志类型

  1. POST 到 /{end},其中 {end} 是日志类型(例如 /web/mobile/api
  2. LoggingHelper 自动在缓冲区中创建新条目,按日期组织

调试日志

  • 查看 uvicorn 控制台输出,了解 add_log() 和 push() 中的打印语句
  • 查看 data/logs/{year}/{month}/{day}/ 中的本地 JSON 文件以获取存储的日志
  • 检查 data/logs/ 中下载的 HF 数据集

修改同步间隔

logging_helper.py 初始化(main.py 第 25-28 行)中调整 synchronize_interval 参数(以秒为单位)

扩展 JWT 有效负载

修改 utils.py 中的 parse_token() 以从 JWT 有效负载中提取其他字段,然后更新 main.py 中 add_log() 中的日志架构