- 新增 Project 模型(repo_url, local_path, name, description) - 项目 CRUD API(GET/PUT /api/v1/projects) - 日志上报自动 upsert Project 记录 - ErrorLog 增加 failure_reason 字段 - update_task_status / create_repair_report 写入失败原因 - Repair Agent 优先从 API 获取项目配置,回退 .env - 新增 Web 端「项目管理」页面(表格 + 行内编辑) - BugList/BugDetail/RepairList 展示失败原因 - 更新接入指南文档 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 KiB
Log Center 接入指南
概述
Log Center 是一个集中式错误日志收集与 AI 自动修复平台,提供 REST API 供各项目接入,实现运行时错误的统一收集、去重、追踪、分析和自动修复。
接入流程:
- 项目首次上报日志时自动注册到 Log Center
- 在 Web 管理端配置项目的仓库地址和本地路径
- 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)
{
"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)
{
"message": "Log reported",
"id": 123
}
已存在 (200) - 重复错误自动去重
{
"message": "Log deduplicated",
"id": 123,
"status": "NEW"
}
项目管理 API
项目在首次日志上报时自动创建,之后可通过 API 或 Web 管理端编辑配置。
获取项目列表
GET /api/v1/projects
{
"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}
{
"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)
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:
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)
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<string, unknown>;
}
export function reportError(error: Error, context?: Record<string, unknown>) {
// 解析堆栈信息
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:
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)
import 'dart:convert';
import 'package:http/http.dart' as http;
const logCenterUrl = String.fromEnvironment(
'LOG_CENTER_URL',
defaultValue: 'http://localhost:8002',
);
Future<void> reportError(dynamic error, StackTrace stackTrace, {Map<String, dynamic>? 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 中全局捕获:
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 修复的详细过程(分析、代码变更、测试结果、失败原因)
最佳实践
- 首次接入时传入
repo_url: 在日志上报中包含仓库地址,省去手动配置步骤 - 设置超时: 上报请求设置 3 秒超时,避免影响主业务
- 静默失败: 上报失败不应影响用户体验
- 异步上报: 使用异步方式上报,不阻塞主流程
- 添加上下文: 尽量添加有用的上下文信息(用户ID、请求URL等)
- 环境区分: 正确设置
environment字段区分开发/生产 - 配置本地路径: 接入后在 Web 端配置
local_path,使 Repair Agent 能正确定位代码
环境变量配置
Python 项目
# .env
LOG_CENTER_URL=http://localhost:8002
ENVIRONMENT=development
REPO_URL=https://gitea.example.com/team/rtc_backend.git # 可选
JavaScript 项目
# .env
VITE_LOG_CENTER_URL=http://localhost:8002
Flutter 项目
# 编译时传入
flutter run --dart-define=LOG_CENTER_URL=http://localhost:8002
flutter run --dart-define=ENVIRONMENT=development
API 文档
完整 API 文档请访问: http://localhost:8002/docs