Files
social-app/docs/plans/PLAN-logging-manager-2026-01-29.md
qzl ad06fe7de4 refactor: align backend layout and supabase infra
Consolidate backend modules/tests under the backend package while syncing Supabase compose/env config and related plans.
2026-02-05 15:13:06 +08:00

177 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plan: FastAPI + Celery 日志管理器系统
**Date:** 2026-01-29
**Author:** AI Assistant
**Status:** Draft
## Overview
构建一个统一、可扩展的日志管理器系统,覆盖 FastAPI 与 Celery worker 的运行时日志,提供结构化 JSON 输出、错误分离、日志轮转与上下文追踪。目标是满足生产环境可观测性需求,便于检索、关联与故障排查,并与当前项目配置体系保持一致。
## Requirements
### Functional
- [ ] 统一管理 FastAPI 与 Celery worker 日志
- [ ] 日志持久化到 `logs/`,错误日志单独输出到 `logs/errors/`
- [ ] 支持按大小或按时间进行日志轮转
- [ ] 结构化日志(JSON),包含时间戳、级别、模块/函数、消息与上下文
- [ ] ERROR/CRITICAL 记录完整堆栈与错误上下文
- [ ] 支持环境差异化配置(dev/test/prod
### Non-Functional
- [ ] 性能:日志写入对请求延迟影响可控,支持异步队列化扩展
- [ ] 安全:避免记录敏感信息,支持字段脱敏
- [ ] 可维护性:模块化、可测试、与现有配置体系一致
## Technical Approach
### 调研摘要
- Python 官方建议使用 `logging` + `dictConfig` 管理多 handler、多 formatter 与过滤器,适用于生产环境配置化管理。
- FastAPI 通常通过中间件注入 request_id 和上下文,并使用结构化日志输出以便集中检索。
- Celery 官方文档建议在自定义场景下关闭 `worker_hijack_root_logger`,通过信号配置自定义 handler。
- 结构化日志库中,structlog 更贴近标准 logging,可与 `logging` 生态协同;loguru 简化配置但替换性强、与 Celery 深度集成时可控性较弱。
- 生产环境推荐 JSON 结构化日志 + 轮转 + 错误分离,并通过外部系统聚合与告警(如 Sentry)。
### 方案对比(至少两种)
| 方案 | 描述 | 优点 | 缺点 | 结论 |
|------|------|------|------|------|
| 方案 Astdlib logging + 自定义 JSON Formatter | 纯标准库实现 JSON formatter + handler/filters | 依赖最少,符合标准库,易与 Celery/FastAPI 集成 | 结构化上下文绑定与 request_id 传递需手写 | 可作为备选最小方案 |
| 方案 Bstdlib logging + structlog | 用 structlog 生成结构化事件,输出到 logging handler | 结构化上下文与 contextvars 支持好,兼容 logging handler | 引入第三方依赖与配置复杂度 | 推荐主方案 |
| 方案 Cloguru | 直接使用 loguru logger | 配置简单、体验好 | 与 Celery/标准 logging 生态整合成本高 | 不推荐作为主方案 |
### 选型结论
- 采用方案 B`logging` 作为底座,structlog 负责结构化事件与上下文绑定;保留可切换到方案 A 的最小实现路径。
- 通过 `dictConfig` 做环境配置,使用 Rotating/TimedRotating handler 支持按大小或时间轮转。
## Implementation Steps
### Phase 1: 基础日志骨架与配置 (3 hours)
1. 新增日志配置模型(Settings 扩展),支持环境、轮转方式与路径配置。
2. 创建日志模块骨架:formatter、handler、filter、context。
3. 集成 `dictConfig` 初始化入口,支持 dev/test/prod 配置切换。
### Phase 2: FastAPI 集成与上下文 (4 hours)
1. 实现请求中间件:生成 `request_id`,绑定用户与请求上下文(IP、路径、方法)。
2. 定义异常处理器:捕获未处理异常并记录堆栈与上下文。
3. 添加应用启动时日志初始化流程。
### Phase 3: Celery 集成 (3 hours)
1. 在 Celery 应用配置中设置 `worker_hijack_root_logger = False`
2. 使用 Celery 信号(`setup_logging``after_setup_task_logger`)初始化日志并注入 task 上下文。
3. 统一日志格式、error 处理与 request_id 关联(如 task_id)。
### Phase 4: 错误分离与轮转策略 (3 hours)
1. 添加 error handler:仅接受 ERROR/CRITICAL,输出到 `logs/errors/`
2. 实现轮转策略配置(按大小、按时间),并提供统一切换配置项。
3. 增加字段脱敏与敏感字段黑名单过滤器。
### Phase 5: 可选增强功能 (4 hours)
1. 日志查询与过滤接口(基础 API + 分页)。
2. 日志聚合统计(按级别/模块/时间窗口)。
3. Sentry 集成与异常告警。
## Files to Modify
| File | Changes |
|------|---------|
| backend/src/core/config/settings.py | 扩展日志相关配置模型 |
## Files to Create
| File | Purpose |
|------|---------|
| backend/src/core/logging/__init__.py | 模块导出与初始化入口 |
| backend/src/core/logging/config.py | dictConfig 构建与环境配置 |
| backend/src/core/logging/formatters.py | JSON formatter 与字段规范 |
| backend/src/core/logging/handlers.py | 文件、控制台、错误 handler |
| backend/src/core/logging/filters.py | 等级过滤、敏感字段脱敏 |
| backend/src/core/logging/context.py | contextvars 绑定与获取 |
| backend/src/core/logging/middleware.py | FastAPI 请求中间件 |
| backend/src/core/logging/celery.py | Celery 日志信号集成 |
| backend/src/core/logging/examples.py | 使用示例(可选) |
## Dependencies
- [ ] structlog: 结构化日志与 contextvars 支持
- [ ] python-json-logger(备选): 若需要纯 logging JSON formatter
- [ ] sentry-sdk(可选): 异常告警与追踪
## 配置示例
```toml
# .env 示例(通过 pydantic settings 读取)
SOCIAL_RUNTIME__LOG_LEVEL=INFO
SOCIAL_RUNTIME__LOG_JSON=true
SOCIAL_RUNTIME__LOG_ROTATION=TIME
SOCIAL_RUNTIME__LOG_ROTATION_WHEN=midnight
SOCIAL_RUNTIME__LOG_ROTATION_BACKUP_COUNT=14
SOCIAL_RUNTIME__LOG_DIR=logs
SOCIAL_RUNTIME__LOG_ERROR_DIR=logs/errors
```
## 使用示例代码
```python
from core.logging import configure_logging, get_logger
configure_logging()
logger = get_logger(__name__)
logger.info("user login", extra={"user_id": "u_123"})
```
## Testing Strategy
- **Unit Tests:** formatter 输出结构、filter 脱敏规则、context 绑定行为
- **Integration Tests:** FastAPI 中间件注入的 request_id 与错误分离写入
- **E2E Tests:** 关键流程触发错误,验证 error 日志输出与轮转
## Test Database 约定
### Supabase 组件能力范围
- Supabase 的 Auth/Storage/Realtime 等组件是独立服务,默认指向同一个主数据库。
- 单独创建一个“测试数据库”(Postgres database)并不会自动获得这些组件的能力,除非显式为这些服务配置新的数据库连接。
- 因此,“测试数据库”默认只具备纯 Postgres 能力;Supabase 组件能力仍然作用在主数据库上。
### 对测试的影响
- **只走直连数据库的测试**(如通过 SQLAlchemy/psycopg 直连)不会受影响。
- **依赖 Supabase 组件的测试**(例如通过 Auth/Storage/Realtime API)会仍然落到主数据库,可能导致:
- 测试数据污染主库
- 并发测试互相干扰
- 若需要 Supabase 组件也“指向测试数据库”,需要启动一套独立 Supabase 栈或重新配置各服务连接(通常不建议在同一栈内动态切换)。
### 环境变量与自动创建
- 建议为“测试数据库”提供独立环境变量(仅测试环境读取),例如:
- `SOCIAL_TEST_DATABASE__HOST`
- `SOCIAL_TEST_DATABASE__PORT`
- `SOCIAL_TEST_DATABASE__NAME`
- `SOCIAL_TEST_DATABASE__USER`
- `SOCIAL_TEST_DATABASE__PASSWORD`
- 若使用 Docker 启动 Postgres,建议在容器初始化阶段自动创建测试数据库(避免手动创建):
- 通过 `docker-entrypoint-initdb.d` 的 init SQL 脚本创建测试数据库与权限
- 保证容器重建后自动恢复测试数据库
- 若使用独立 Supabase 栈做测试,测试环境变量应指向该栈的数据库与服务端口。
## Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| Celery 日志被自动劫持导致重复或丢失 | High | Medium | 设置 `worker_hijack_root_logger=False` 并通过信号统一配置 |
| 结构化字段不一致导致下游解析失败 | Medium | Medium | 统一 schema,增加单元测试与校验 |
| 误记录敏感信息 | High | Medium | 增加脱敏过滤器与字段黑名单 |
| 日志量过大影响性能 | Medium | Medium | 轮转 + 级别控制 + 可选异步队列化 |
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Phase 1 | 3 hours |
| Phase 2 | 4 hours |
| Phase 3 | 3 hours |
| Phase 4 | 3 hours |
| Phase 5 | 4 hours |
| **Total** | **17 hours** |