log-center/docs/integration_guide.md
zyc 625e53dc44
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 2m16s
feat(project-mgmt): 项目管理 + 失败原因追踪 + 前端展示
- 新增 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>
2026-02-24 11:18:27 +08:00

12 KiB
Raw Blame History

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)

{
  "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_urllocal_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.pycustom_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 修复的详细过程(分析、代码变更、测试结果、失败原因)

最佳实践

  1. 首次接入时传入 repo_url: 在日志上报中包含仓库地址,省去手动配置步骤
  2. 设置超时: 上报请求设置 3 秒超时,避免影响主业务
  3. 静默失败: 上报失败不应影响用户体验
  4. 异步上报: 使用异步方式上报,不阻塞主流程
  5. 添加上下文: 尽量添加有用的上下文信息用户ID、请求URL等
  6. 环境区分: 正确设置 environment 字段区分开发/生产
  7. 配置本地路径: 接入后在 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