# Log Center 接入指南 ## 概述 Log Center 是一个集中式错误日志收集与 AI 自动修复平台,提供 REST API 供各项目接入,实现运行时错误的统一收集、去重、追踪、分析和自动修复。 接入流程: 1. 项目首次上报日志时自动注册到 Log Center 2. 在 Web 管理端配置项目的**仓库地址**和**本地路径** 3. Repair Agent 根据配置自动拉取代码并修复 Bug --- ## 快速开始 ### 服务地址 | 环境 | API 地址 | 仪表盘 | |------|----------|--------| | 本地开发 | `http://localhost:8002` | `http://localhost:8003` | | 生产环境 | `https://qiyuan-log-center-api.airlabs.art` | `https://qiyuan-log-center.airlabs.art` | --- ## API 接口 ### 上报错误日志 **POST** `/api/v1/logs/report` #### 请求体 (JSON) ```json { "project_id": "rtc_backend", "environment": "production", "level": "ERROR", "timestamp": "2026-01-30T10:30:00Z", "version": "1.2.3", "commit_hash": "abc1234", "repo_url": "https://gitea.example.com/team/rtc_backend.git", "error": { "type": "ValueError", "message": "invalid literal for int() with base 10: 'abc'", "file_path": "apps/users/views.py", "line_number": 42, "stack_trace": [ "Traceback (most recent call last):", " File \"apps/users/views.py\", line 42, in get_user", " user_id = int(request.GET['id'])", "ValueError: invalid literal for int() with base 10: 'abc'" ] }, "context": { "url": "/api/users/123", "method": "GET", "user_id": "u_12345", "request_id": "req_abc123" } } ``` #### 字段说明 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `project_id` | string | ✅ | 项目标识,如 `rtc_backend`, `rtc_web`, `airhub_app` | | `environment` | string | ✅ | 环境:`development`, `staging`, `production` | | `level` | string | ✅ | 日志级别:`ERROR`, `WARNING`, `CRITICAL` | | `source` | string | ❌ | 来源:`runtime`(默认), `cicd`, `deployment` | | `timestamp` | string | ❌ | ISO 8601 格式,不传则使用服务器时间 | | `version` | string | ❌ | 应用版本号 | | `commit_hash` | string | ❌ | Git commit hash | | `repo_url` | string | ❌ | 项目仓库地址,首次上报时传入可自动关联到项目 | | `error.type` | string | ✅ | 异常类型,如 `ValueError`, `TypeError` | | `error.message` | string | ✅ | 错误消息 | | `error.file_path` | string | ❌ | 出错文件路径(runtime 必填,cicd/deployment 可选) | | `error.line_number` | int | ❌ | 出错行号(runtime 必填,cicd/deployment 可选) | | `error.stack_trace` | array | ✅ | 堆栈信息(数组或字符串) | | `context` | object | ❌ | 额外上下文信息(URL、用户ID等) | > **项目自动注册**: 首次上报日志时,系统会根据 `project_id` 自动创建项目记录。如果同时传入 `repo_url`,会自动关联仓库地址,供 Repair Agent 使用。 #### 响应 **成功 (200)** ```json { "message": "Log reported", "id": 123 } ``` **已存在 (200)** - 重复错误自动去重 ```json { "message": "Log deduplicated", "id": 123, "status": "NEW" } ``` --- ### 项目管理 API 项目在首次日志上报时自动创建,之后可通过 API 或 Web 管理端编辑配置。 #### 获取项目列表 **GET** `/api/v1/projects` ```json { "projects": [ { "id": 1, "project_id": "rtc_backend", "name": "RTC 后端", "repo_url": "https://gitea.example.com/team/rtc_backend.git", "local_path": "/home/dev/projects/rtc_backend", "description": "Django 后端服务", "created_at": "2026-01-15T08:00:00", "updated_at": "2026-02-20T10:30:00" } ] } ``` #### 获取项目详情 **GET** `/api/v1/projects/{project_id}` 返回单个项目的完整信息。 #### 编辑项目配置 **PUT** `/api/v1/projects/{project_id}` ```json { "name": "RTC 后端", "repo_url": "https://gitea.example.com/team/rtc_backend.git", "local_path": "/home/dev/projects/rtc_backend", "description": "Django 后端服务" } ``` | 字段 | 类型 | 说明 | |------|------|------| | `name` | string | 项目显示名称 | | `repo_url` | string | Git 仓库地址(Repair Agent 克隆/推送代码用) | | `local_path` | string | 本地项目路径(Repair Agent 在此目录执行修复) | | `description` | string | 项目描述 | > **注意**: `repo_url` 和 `local_path` 是 Repair Agent 正常工作的关键配置。未配置时 Agent 将无法执行 Git 操作或定位项目代码。可在 Web 管理端的「项目管理」页面中配置。 --- ## 接入示例 ### Python (Django / FastAPI) ```python import requests import traceback import os LOG_CENTER_URL = os.getenv("LOG_CENTER_URL", "http://localhost:8002") def report_error(exc, context=None): """上报错误到 Log Center""" tb = traceback.extract_tb(exc.__traceback__) last_frame = tb[-1] if tb else None payload = { "project_id": "rtc_backend", "environment": os.getenv("ENVIRONMENT", "development"), "level": "ERROR", "repo_url": os.getenv("REPO_URL", ""), # 可选:关联仓库地址 "error": { "type": type(exc).__name__, "message": str(exc), "file_path": last_frame.filename if last_frame else "unknown", "line_number": last_frame.lineno if last_frame else 0, "stack_trace": traceback.format_exception(exc) }, "context": context or {} } try: requests.post( f"{LOG_CENTER_URL}/api/v1/logs/report", json=payload, timeout=3 # 快速失败,不影响主业务 ) except Exception: pass # 静默失败,不影响主业务 ``` #### Django 集成位置 修改 `utils/exceptions.py` 的 `custom_exception_handler`: ```python def custom_exception_handler(exc, context): # 上报到 Log Center (异步,不阻塞响应) report_error(exc, { "view": str(context.get("view")), "request_path": context.get("request").path if context.get("request") else None, }) # ... 原有逻辑不变 ... ``` --- ### JavaScript / TypeScript (React / Vue) ```typescript const LOG_CENTER_URL = import.meta.env.VITE_LOG_CENTER_URL || 'http://localhost:8002'; interface ErrorPayload { project_id: string; environment: string; level: string; repo_url?: string; error: { type: string; message: string; file_path: string; line_number: number; stack_trace: string[]; }; context?: Record; } export function reportError(error: Error, context?: Record) { // 解析堆栈信息 const stackLines = error.stack?.split('\n') || []; const match = stackLines[1]?.match(/at\s+.*\s+\((.+):(\d+):\d+\)/); const payload: ErrorPayload = { project_id: 'rtc_web', environment: import.meta.env.MODE, level: 'ERROR', error: { type: error.name, message: error.message, file_path: match?.[1] || 'unknown', line_number: parseInt(match?.[2] || '0'), stack_trace: stackLines, }, context: { url: window.location.href, userAgent: navigator.userAgent, ...context, }, }; // 使用 sendBeacon 确保页面关闭时也能发送 if (navigator.sendBeacon) { navigator.sendBeacon( `${LOG_CENTER_URL}/api/v1/logs/report`, JSON.stringify(payload) ); } else { fetch(`${LOG_CENTER_URL}/api/v1/logs/report`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), keepalive: true, }).catch(() => {}); } } ``` #### Axios 拦截器集成 修改 `src/api/request.ts`: ```typescript request.interceptors.response.use( (response) => { /* ... */ }, (error: AxiosError) => { // 上报到 Log Center reportError(error, { url: error.config?.url, method: error.config?.method, status: error.response?.status, }); // ... 原有逻辑不变 ... } ); ``` --- ### Flutter (Dart) ```dart import 'dart:convert'; import 'package:http/http.dart' as http; const logCenterUrl = String.fromEnvironment( 'LOG_CENTER_URL', defaultValue: 'http://localhost:8002', ); Future reportError(dynamic error, StackTrace stackTrace, {Map? context}) async { final stackLines = stackTrace.toString().split('\n'); // 解析第一行获取文件和行号 final match = RegExp(r'#0\s+.*\((.+):(\d+):\d+\)').firstMatch(stackLines.first); final payload = { 'project_id': 'airhub_app', 'environment': const String.fromEnvironment('ENVIRONMENT', defaultValue: 'development'), 'level': 'ERROR', 'repo_url': 'https://gitea.example.com/team/airhub_app.git', 'error': { 'type': error.runtimeType.toString(), 'message': error.toString(), 'file_path': match?.group(1) ?? 'unknown', 'line_number': int.tryParse(match?.group(2) ?? '0') ?? 0, 'stack_trace': stackLines.take(20).toList(), }, 'context': context ?? {}, }; try { await http.post( Uri.parse('$logCenterUrl/api/v1/logs/report'), headers: {'Content-Type': 'application/json'}, body: jsonEncode(payload), ).timeout(const Duration(seconds: 3)); } catch (_) { // 静默失败 } } ``` 在 `main.dart` 中全局捕获: ```dart void main() { FlutterError.onError = (details) { reportError(details.exception, details.stack ?? StackTrace.current); }; runZonedGuarded(() { runApp(const MyApp()); }, (error, stack) { reportError(error, stack); }); } ``` --- ## 错误去重机制 Log Center 使用 **指纹(fingerprint)** 对错误进行去重,按来源使用不同的指纹策略: | 来源 | 指纹组成 | |------|----------| | `runtime` | `MD5(project_id \| error_type \| file_path \| line_number)` | | `cicd` | `MD5(project_id \| cicd \| error_type \| job_name \| step_name)` | | `deployment` | `MD5(project_id \| deployment \| error_type \| namespace \| deployment_name)` | 相同指纹的错误只会记录一次。如果已修复的错误再次出现,系统会自动重新打开(回归检测)。 --- ## 错误状态流转 ``` NEW → VERIFYING → PENDING_FIX → FIXING → FIXED → VERIFIED → DEPLOYED ↓ ↓ CANNOT_REPRODUCE FIX_FAILED ``` | 状态 | 说明 | |------|------| | `NEW` | 新上报的错误 | | `VERIFYING` | 正在验证复现 | | `CANNOT_REPRODUCE` | 无法复现 | | `PENDING_FIX` | 等待修复 | | `FIXING` | AI Agent 正在修复中 | | `FIXED` | 已修复,待验证 | | `VERIFIED` | 已验证修复 | | `DEPLOYED` | 已部署上线 | | `FIX_FAILED` | 修复失败(失败原因会记录到数据库并在 Web 端展示) | --- ## Web 管理端 ### 项目管理 访问 Web 管理端的「项目管理」页面,可以: - 查看所有已注册项目及其配置状态 - 编辑项目的**仓库地址**(`repo_url`)和**本地路径**(`local_path`) - 未配置的字段会标红提示 > Repair Agent 依赖这两个配置来定位项目代码和执行 Git 操作。请确保在接入后及时配置。 ### 缺陷追踪 - **缺陷列表**: 按项目、来源、状态筛选,修复失败的缺陷会直接显示失败原因 - **缺陷详情**: 查看完整错误信息、堆栈、上下文,以及修复历史记录 - **修复报告**: 查看每轮 AI 修复的详细过程(分析、代码变更、测试结果、失败原因) --- ## 最佳实践 1. **首次接入时传入 `repo_url`**: 在日志上报中包含仓库地址,省去手动配置步骤 2. **设置超时**: 上报请求设置 3 秒超时,避免影响主业务 3. **静默失败**: 上报失败不应影响用户体验 4. **异步上报**: 使用异步方式上报,不阻塞主流程 5. **添加上下文**: 尽量添加有用的上下文信息(用户ID、请求URL等) 6. **环境区分**: 正确设置 `environment` 字段区分开发/生产 7. **配置本地路径**: 接入后在 Web 端配置 `local_path`,使 Repair Agent 能正确定位代码 --- ## 环境变量配置 ### Python 项目 ```bash # .env LOG_CENTER_URL=http://localhost:8002 ENVIRONMENT=development REPO_URL=https://gitea.example.com/team/rtc_backend.git # 可选 ``` ### JavaScript 项目 ```bash # .env VITE_LOG_CENTER_URL=http://localhost:8002 ``` ### Flutter 项目 ```bash # 编译时传入 flutter run --dart-define=LOG_CENTER_URL=http://localhost:8002 flutter run --dart-define=ENVIRONMENT=development ``` --- ## API 文档 完整 API 文档请访问: [http://localhost:8002/docs](http://localhost:8002/docs)