# 日志系统 API

**版本**: v1.1.0  
**基础路径**: `/api/v1/logs`  
**状态**: ✅ HTTP Header 规范已实现 | 📋 管理 API 规划中  
**最后更新**: 2025-12-12

---

## 概述

日志系统作为技术基础设施，主要通过配置和拦截器工作。所有接口遵循 FFOA 统一 API 规范。

本文档定义：

1. **HTTP Header 规范** - 追踪 ID 传递标准（已实现）
2. **日志管理 API** - 查询、配置、告警接口（规划中）

### 通用约定

| 约定 | 说明 |
|------|------|
| **基础路径** | `/api/v1/logs` |
| **时间格式** | UTC ISO 8601（如 `2025-12-07T10:30:00.123Z`），前端负责本地化 |
| **认证方式** | Bearer Token（`Authorization: Bearer <token>`） |
| **请求头** | `Content-Type: application/json` |
| **响应格式** | 统一 `{ success, data, message?, timestamp, path }` 结构 |

### API 受众与使用场景

> **重要区分**: 日志系统分为 **Data Plane** 和 **Control Plane** 两部分。

| 层面 | 面向对象 | 说明 |
|------|---------|------|
| **Data Plane**（数据面） | 所有业务服务 | HTTP Header 规范（`X-Trace-Id` 等），由框架自动处理 |
| **Control Plane**（控制面） | 内部管理后台 / 运维工具 | `/api/v1/logs/*` API，不对外部业务系统开放 |

**使用规范**：

- ✅ 外部业务系统：只依赖 **Header 规范**，框架自动注入 `X-Trace-Id`、`X-Request-Id`
- ✅ 内部管理后台：可调用 `/api/v1/logs/*` API 进行日志查询、配置管理
- ❌ 业务服务代码：**不应直接调用** `/api/v1/logs/*` API

---

## 认证与权限

### 角色权限矩阵

| 角色 | 可访问接口 | 数据范围 | 说明 |
|------|-----------|---------|------|
| `admin` | 所有接口 | 全部日志、配置管理 | 平台管理员 |
| `ops` | 统计、告警、清理接口 | 运维相关数据 | 运维人员 |
| `developer` | 查询、追踪接口 | 仅开发环境，无配置权限 | 开发人员 |

### 权限装饰器

```typescript
@RequirePermissions('log:read')    // 基础查询
@RequirePermissions('log:config')  // 配置管理
@RequirePermissions('log:cleanup') // 日志清理
@RequirePermissions('log:alert')   // 告警管理
```

---

## 公共类型定义

### 统一响应格式

#### 成功响应

```typescript
interface ApiResponse<T> {
  success: true;
  data: T;
  message?: string;
  timestamp: string;       // ISO 8601 格式
  path: string;            // 请求路径
}
```

#### 分页响应

```typescript
interface PaginatedResponse<T> {
  success: true;
  data: {
    items: T[];
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
  timestamp: string;
  path: string;
}
```

#### 错误响应

```typescript
interface ApiError {
  success: false;
  error: {
    code: string;          // 机器可读错误码
    message: string;       // 人类可读消息
    details?: any;         // 详细信息
    field?: string;        // 字段级错误
    errors?: FieldError[]; // 多字段验证错误
  };
  timestamp: string;
  path: string;
  method: string;
  statusCode: number;
}

interface FieldError {
  field: string;
  message: string;
  value?: any;
  constraint?: string;
}
```

### 通用查询参数

```typescript
interface CommonQueryParams {
  // 时间范围
  startTime?: string;        // ISO 8601 格式
  endTime?: string;          // ISO 8601 格式
  
  // 服务/区域
  service?: LogService;      // 服务标识（推荐枚举，见下方说明）
  region?: string;           // CN | US | UAE
  
  // 排序（仅允许指定字段）
  sortBy?: '@timestamp' | 'level' | 'duration' | 'service';  // 默认 '@timestamp'
  sortOrder?: 'asc' | 'desc';                                 // 默认 'desc'
  
  // 分页
  page?: number;             // 默认 1
  limit?: number;            // 默认 50，最大 200
}
```

**排序字段限制**：
- `sortBy` 仅允许：`@timestamp`、`level`、`duration`、`service`
- 默认：`sortBy = '@timestamp'`，`sortOrder = 'desc'`（最新日志在最前）

### 多区域（Region）权限与默认行为

> **说明**: 日志查询支持多区域隔离，权限控制如下：

| 角色 | region 参数行为 | 说明 |
|------|----------------|------|
| **区域运维**（如 CN Ops） | 可选，但自动限定为授权区域 | 即使不传 `region`，也只能查询 `CN` 的日志 |
| **平台 Admin** | 可选，不传则查询所有区域 | 必须配合时间范围和分页限制 |

**实现建议**：

```typescript
// 后端自动注入 region 限制
if (user.role === 'ops' && user.regions) {
  query.region = user.regions; // 强制限定区域
}
```

**前端注意**：
- 如果 UI 没有显示某个区域的日志，请先确认当前用户是否有该区域权限
- 平台 Admin 查询所有区域时，建议在 UI 强制要求选择时间范围

### 数据脱敏说明

> **重要**: 日志中的敏感字段（密码、Token、身份证号等）在**写入时**即已脱敏，API 返回的是脱敏后的数据（如 `***REDACTED***`），不会通过 API 返回原文。

---

## 时间跨度限制速查表

| 接口 | 时间跨度限制 | 说明 |
|------|-------------|------|
| `GET /logs/query` | ≤ 7 天 | 通用查询，防止大范围全量扫描 |
| `GET /logs/errors` | ≤ 7 天 | 错误日志查询 |
| `GET /logs/slow-requests` | ≤ 7 天 | 慢请求查询 |
| `GET /logs/stats` | ≤ 30 天 | 统计分析 |
| `GET /logs/alerts` | ≤ 30 天 | 告警历史查询 |

**防止全量扫描**：

> 为避免 UI 出现「查询所有日志」导致的性能问题，实现时建议：
> - `startTime` 必填，或给一个默认时间窗口（如最近 1 小时）
> - 不允许 `无过滤条件 + 大时间范围 + 大 limit` 的组合查询
> - 超过限制时返回 `LOG_QUERY_TOO_BROAD` 错误

---

## HTTP Header 规范（✅ 已实现）

> **Data Plane**: 此部分面向所有业务服务，由框架自动处理。

### 请求 Header

| Header | 方向 | 必填 | 说明 |
|--------|------|------|------|
| `X-Trace-Id` | 入站 | 可选 | 追踪 ID，跨服务传递，不存在则自动生成 |
| `X-Span-Id` | 入站 | 可选 | 父 Span ID，下游服务接收 |

### 响应 Header

所有 API 响应会自动包含以下 Header：

| Header | 说明 | 示例 |
|--------|------|------|
| `X-Request-Id` | 请求 ID（单服务内唯一） | `1733580000123-abc` |
| `X-Trace-Id` | 追踪 ID（跨服务） | `CN-1733580000000-a1b2c3d4` |
| `X-Response-Time` | 响应时间 | `123ms` |

### ID 格式规范

| ID 类型 | 格式 | 示例 | 用途 |
|---------|------|------|------|
| RequestID | `{timestamp}-{random6}` | `1733580000123-abc` | 单服务内唯一，用于 grep |
| traceId | `{region}-{timestamp}-{random8}` | `CN-1733580000000-a1b2c3d4` | 跨服务追踪，通过 header 传递 |
| spanId | `{service}-{timestamp}-{random6}` | `FORM-1733580000123-x1y2z3` | 当前操作标识 |

### 使用示例

```bash
# 发起请求时可传入 traceId（可选）
curl -X POST 'http://localhost:3001/api/v1/forms' \
  -H 'Authorization: Bearer <token>' \
  -H 'X-Trace-Id: CN-1733580000000-a1b2c3d4'

# 响应会包含追踪信息
# X-Request-Id: 1733580000456-xyz
# X-Trace-Id: CN-1733580000000-a1b2c3d4
# X-Response-Time: 234ms
```

---

## API 列表

> **Control Plane**: 以下 API 面向内部管理后台和运维工具。

### 日志查询（✅ 已实现）

| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/api/v1/logs/query` | 查询日志 | `log:read` |
| GET | `/api/v1/logs/trace/:traceId` | 按追踪 ID 查询完整链路 | `log:read` |
| GET | `/api/v1/logs/errors` | 查询错误日志 | `log:read` |
| GET | `/api/v1/logs/slow-requests` | 查询慢请求 | `log:read` |

### 日志管理（✅ 已实现）

| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/api/v1/logs/config` | 获取日志配置 | `log:config` |
| PUT | `/api/v1/logs/config` | 更新日志配置 | `log:config` |
| POST | `/api/v1/logs/cleanup` | 手动清理日志 | `log:cleanup` |
| GET | `/api/v1/logs/stats` | 日志统计信息 | `log:read` |

### 告警管理（✅ 已实现）

| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/api/v1/logs/alerts` | 获取告警历史 | `log:alert` |
| GET | `/api/v1/logs/alerts/config` | 获取告警配置 | `log:alert` |
| PUT | `/api/v1/logs/alerts/config` | 更新告警配置 | `log:alert` |
| POST | `/api/v1/logs/alerts/test` | 测试告警通道 | `log:alert` |

---

## 接口详情

### GET /api/v1/logs/query

查询日志列表。

#### 请求参数（Query）

```typescript
interface QueryLogDto extends CommonQueryParams {
  // 过滤条件
  level?: LogLevel;          // 日志级别（枚举）
  service?: LogService;      // 服务标识（推荐枚举，见下方说明）
  traceId?: string;          // 追踪 ID
  requestId?: string;        // 请求 ID
  userId?: string;           // 用户 ID
  url?: string;              // URL 模糊匹配
  
  // 关键词搜索（在 message、error.message、user.name 中模糊搜索）
  keyword?: string;
  
  // 高级过滤
  minDuration?: number;      // 最小响应时间（ms）
  maxDuration?: number;      // 最大响应时间（ms）
  statusCode?: number;       // HTTP 状态码
  hasError?: boolean;        // 是否有错误
}
```

#### keyword 搜索说明

`keyword` 参数会在以下字段中进行**模糊搜索**（ILIKE）：

| 搜索字段 | 说明 | 示例 |
|---------|------|------|
| `message` | 日志消息 | "POST /api/v1/forms"、"Database connection failed" |
| `error.message` | 错误消息 | "Connection refused"、"Timeout" |
| `user.name` | 操作人用户名 | "zhangsan"、"admin" |

**前端建议**：提供搜索框让用户输入关键词，可同时匹配日志内容和操作人。

#### 约束说明

- `startTime` 和 `endTime` 之间跨度最大为 **7 天**，超出返回 `LOG_QUERY_TIME_RANGE_TOO_LARGE`
- 如果不传 `startTime`，默认查询最近 **1 小时**的日志
- 不允许无过滤条件的全量查询，至少需要指定 `startTime` 或 `level` 或 `service`
- 默认排序：`@timestamp` 字段按**降序**排列
- `limit` 最大值为 200

#### 响应

```typescript
interface LogListResponse {
  success: true;
  data: {
    items: LogItem[];
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
  timestamp: string;
  path: string;
}

interface LogItem {
  '@timestamp': string;     // ISO 8601
  level: LogLevel;
  message: string;
  
  // 追踪信息
  trace: {
    id: string;             // traceId
    span_id: string;        // spanId
    parent_span_id?: string;
  };
  
  // HTTP 信息
  http: {
    request_id: string;
    method: string;
    url: string;
    status_code: number;
    duration_ms: number;
  };
  
  // 用户信息
  user?: {
    id: string;
    name: string;
  };
  
  // 客户端信息
  client: {
    ip: string;
    user_agent: string;
  };
  
  // 服务信息
  service: {
    name: string;
    region: string;
    instance: string;
  };
  
  // 错误信息（如果有）
  error?: {
    type: string;
    message: string;
    code?: string;
    stack?: string;         // 仅开发环境
  };
}
```

#### 示例

**请求**

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/query?page=1&limit=20&level=ERROR&startTime=2025-12-07T00:00:00.000Z&endTime=2025-12-07T23:59:59.999Z' \
  -H 'Authorization: Bearer <token>'
```

**响应**

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "@timestamp": "2025-12-07T10:30:00.123Z",
        "level": "ERROR",
        "message": "POST /api/v1/forms | 500 | 1234ms",
        "trace": {
          "id": "CN-1733580000000-a1b2c3d4",
          "span_id": "FORM-1733580000123-x1y2z3"
        },
        "http": {
          "request_id": "1733580000123-abc",
          "method": "POST",
          "url": "/api/v1/forms",
          "status_code": 500,
          "duration_ms": 1234
        },
        "user": {
          "id": "user-001",
          "name": "zhangsan"
        },
        "client": {
          "ip": "192.168.1.100",
          "user_agent": "Mozilla/5.0..."
        },
        "service": {
          "name": "Form",
          "region": "CN",
          "instance": "form-pod-1"
        },
        "error": {
          "type": "InternalServerError",
          "message": "Database connection failed"
        }
      }
    ],
    "total": 1234,
    "page": 1,
    "limit": 20,
    "totalPages": 62,
    "hasNext": true,
    "hasPrev": false
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/query"
}
```

---

### GET /api/v1/logs/trace/:traceId

查询一个 traceId 下的完整调用链路，包含所有服务的 span。

#### 路径参数

| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| traceId | string | 是 | 追踪 ID |

#### 响应

```typescript
interface TraceResponse {
  success: true;
  data: {
    traceId: string;
    startTime: string;
    endTime: string;
    totalDuration: number;
    spans: SpanInfo[];
    timeline: TimelineEvent[];
  };
  timestamp: string;
  path: string;
}

interface SpanInfo {
  spanId: string;
  parentSpanId: string | null;
  service: string;
  region: string;
  operation: string;
  startTime: string;
  duration: number;
  status: 'SUCCESS' | 'ERROR';
}

interface TimelineEvent {
  time: number;            // 相对于 trace 开始时间的偏移（ms）
  event: string;
}
```

#### 响应字段用途说明

| 字段 | 推荐用途 | 前端实现建议 |
|------|---------|-------------|
| `spans` | 渲染**瀑布图 / 甘特图**式的 trace 视图 | 用 `parentSpanId` 构建树形结构，用 `startTime` + `duration` 画时间条 |
| `timeline` | 渲染**文字版事件流**，方便非技术人员理解 | 直接用表格或时间轴组件展示 |

#### 示例

**请求**

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/trace/CN-1733580000000-a1b2c3d4' \
  -H 'Authorization: Bearer <token>'
```

**响应**

```json
{
  "success": true,
  "data": {
    "traceId": "CN-1733580000000-a1b2c3d4",
    "startTime": "2025-12-07T10:30:00.100Z",
    "endTime": "2025-12-07T10:30:00.350Z",
    "totalDuration": 250,
    "spans": [
      {
        "spanId": "IAM-1733580000100-m1n2o3",
        "parentSpanId": null,
        "service": "IAM",
        "region": "CN",
        "operation": "POST /api/v1/auth/verify",
        "startTime": "2025-12-07T10:30:00.100Z",
        "duration": 50,
        "status": "SUCCESS"
      },
      {
        "spanId": "FORM-1733580000123-x1y2z3",
        "parentSpanId": "IAM-1733580000100-m1n2o3",
        "service": "Form",
        "region": "CN",
        "operation": "POST /api/v1/forms",
        "startTime": "2025-12-07T10:30:00.150Z",
        "duration": 150,
        "status": "SUCCESS"
      },
      {
        "spanId": "APPROVAL-1733580000200-p1q2r3",
        "parentSpanId": "FORM-1733580000123-x1y2z3",
        "service": "Approval",
        "region": "CN",
        "operation": "POST /api/v1/approval/start",
        "startTime": "2025-12-07T10:30:00.300Z",
        "duration": 50,
        "status": "SUCCESS"
      }
    ],
    "timeline": [
      { "time": 0, "event": "IAM auth verify started" },
      { "time": 50, "event": "IAM auth verify completed" },
      { "time": 50, "event": "Form create started" },
      { "time": 200, "event": "Form create completed" },
      { "time": 200, "event": "Approval start started" },
      { "time": 250, "event": "Approval start completed" }
    ]
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/trace/CN-1733580000000-a1b2c3d4"
}
```

---

### GET /api/v1/logs/errors

查询错误日志列表。

#### 请求参数（Query）

```typescript
interface QueryErrorLogDto extends CommonQueryParams {
  errorType?: string;      // 错误类型
  errorCode?: string;      // 错误代码
  userId?: string;         // 用户 ID
  url?: string;            // URL 模糊匹配
  
  // 关键词搜索（在 error.message、message 中模糊搜索）
  keyword?: string;
}
```

#### 约束说明

- `startTime` 和 `endTime` 之间跨度最大为 **7 天**

#### 响应

```typescript
interface ErrorLogListResponse {
  success: true;
  data: {
    items: ErrorLogItem[];
    summary: {
      total: number;
      byType: Record<string, number>;
      byHour: { hour: string; count: number }[];
    };
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
  timestamp: string;
  path: string;
}
```

#### 示例

**请求**

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/errors?userId=user-001&startTime=2025-12-07T00:00:00Z' \
  -H 'Authorization: Bearer <token>'
```

**响应**

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "@timestamp": "2025-12-07T10:30:00.123Z",
        "level": "ERROR",
        "message": "Database connection failed",
        "trace": {
          "id": "CN-1733580000000-a1b2c3d4"
        },
        "http": {
          "request_id": "1733580000123-abc",
          "method": "POST",
          "url": "/api/v1/forms",
          "status_code": 500,
          "duration_ms": 1234
        },
        "error": {
          "type": "DatabaseError",
          "code": "CONNECTION_REFUSED",
          "message": "Failed to connect to database",
          "stack": "Error: Failed to connect...\n    at ..."
        },
        "user": {
          "id": "user-001",
          "name": "zhangsan"
        }
      }
    ],
    "summary": {
      "total": 150,
      "byType": {
        "DatabaseError": 50,
        "ValidationError": 40,
        "AuthenticationError": 30,
        "InternalServerError": 30
      },
      "byHour": [
        { "hour": "10:00", "count": 20 },
        { "hour": "11:00", "count": 15 }
      ]
    },
    "total": 150,
    "page": 1,
    "limit": 50,
    "totalPages": 3,
    "hasNext": true,
    "hasPrev": false
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/errors"
}
```

---

### GET /api/v1/logs/slow-requests

查询慢请求列表。

#### 请求参数（Query）

```typescript
interface QuerySlowRequestDto extends CommonQueryParams {
  thresholdMs?: number;    // 慢请求阈值，默认 2000
  url?: string;            // URL 模糊匹配
  userId?: string;         // 用户 ID
}
```

#### 响应

```typescript
interface SlowRequestListResponse {
  success: true;
  data: {
    items: SlowRequestItem[];
    summary: {
      total: number;
      avgDuration: number;
      maxDuration: number;
      p95Duration: number;
      p99Duration: number;
      topPaths: { path: string; count: number; avgDuration: number }[];
      byService: Record<string, number>;
    };
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
  timestamp: string;
  path: string;
}
```

#### 示例

**请求**

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/slow-requests?thresholdMs=3000&service=Form' \
  -H 'Authorization: Bearer <token>'
```

**响应**

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "timestamp": "2025-12-07T10:30:00.123Z",
        "traceId": "CN-1733580000000-a1b2c3d4",
        "requestId": "1733580000123-abc",
        "method": "POST",
        "url": "/api/v1/forms/submit",
        "duration": 5234,
        "user": {
          "id": "user-001",
          "name": "zhangsan"
        },
        "service": "Form",
        "region": "CN"
      }
    ],
    "summary": {
      "total": 15,
      "avgDuration": 3456,
      "maxDuration": 8234,
      "p95Duration": 5500,
      "p99Duration": 7800,
      "topPaths": [
        { "path": "/api/v1/forms/submit", "count": 5, "avgDuration": 4500 },
        { "path": "/api/v1/reports/generate", "count": 3, "avgDuration": 6000 },
        { "path": "/api/v1/files/upload", "count": 2, "avgDuration": 5000 }
      ],
      "byService": {
        "Form": 8,
        "Approval": 5,
        "IAM": 2
      }
    },
    "total": 15,
    "page": 1,
    "limit": 50,
    "totalPages": 1,
    "hasNext": false,
    "hasPrev": false
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/slow-requests"
}
```

---

### GET /api/v1/logs/config

获取当前日志配置。

#### 响应

```typescript
interface LogConfigResponse {
  success: true;
  data: LogConfig;
  timestamp: string;
  path: string;
}

interface LogConfig {
  level: LogLevel;
  sampling: {
    enabled: boolean;
    rules: {
      alwaysLog: { levels: LogLevel[]; paths: string[] };
      neverLog: { paths: string[] };
      dynamic: {
        qpsThreshold: number;
        minSampleRate: number;
        maxSampleRate: number;
      };
    };
  };
  retention: {
    http: number;          // 天
    application: number;   // 天
    error: number;         // 天
  };
  alerts: AlertConfig;
  updatedAt: string;
  updatedBy: string;
}
```

#### 示例

**响应**

```json
{
  "success": true,
  "data": {
    "level": "INFO",
    "sampling": {
      "enabled": true,
      "rules": {
        "alwaysLog": {
          "levels": ["ERROR", "WARN"],
          "paths": ["/api/v1/auth/*"]
        },
        "neverLog": {
          "paths": ["/health", "/ready", "/metrics"]
        },
        "dynamic": {
          "qpsThreshold": 1000,
          "minSampleRate": 0.1,
          "maxSampleRate": 1.0
        }
      }
    },
    "retention": {
      "http": 14,
      "application": 30,
      "error": 30
    },
    "alerts": {
      "slowRequest": {
        "enabled": true,
        "thresholdMs": 2000,
        "excludePaths": ["/api/v1/files/upload"]
      },
      "errorRate": {
        "enabled": true,
        "thresholdPercent": 5,
        "windowMinutes": 5
      },
      "diskSpace": {
        "enabled": true,
        "warningPercent": 80,
        "criticalPercent": 90
      }
    },
    "updatedAt": "2025-12-07T10:30:00.123Z",
    "updatedBy": "admin"
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/config"
}
```

---

### PUT /api/v1/logs/config

更新日志配置。

#### 请求体

```typescript
interface UpdateLogConfigDto {
  level?: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
  
  sampling?: {
    enabled?: boolean;
    rules?: {
      dynamic?: {
        qpsThreshold?: number;
        minSampleRate?: number;
        maxSampleRate?: number;
      };
    };
  };
  
  alerts?: {
    slowRequest?: {
      enabled?: boolean;
      thresholdMs?: number;
      excludePaths?: string[];
    };
    errorRate?: {
      enabled?: boolean;
      thresholdPercent?: number;
      windowMinutes?: number;
    };
    diskSpace?: {
      enabled?: boolean;
      warningPercent?: number;
      criticalPercent?: number;
    };
  };
}
```

#### 示例

**请求**

```bash
curl -X PUT 'http://localhost:3001/api/v1/logs/config' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"level": "DEBUG"}'
```

**响应**

```json
{
  "success": true,
  "data": {
    "level": "DEBUG",
    "sampling": {
      "enabled": true
    },
    "alerts": {
      "slowRequest": {
        "enabled": true,
        "thresholdMs": 3000
      }
    },
    "updatedAt": "2025-12-07T10:35:00.123Z",
    "updatedBy": "admin"
  },
  "message": "日志配置已更新",
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/config"
}
```

---

### POST /api/v1/logs/cleanup

手动清理日志文件。

#### 请求体

```typescript
interface CleanupLogDto {
  type?: 'http' | 'application' | 'error' | 'all';  // 默认 'all'
  olderThanDays?: number;    // 清理多少天前的日志
  dryRun?: boolean;          // 试运行，不实际删除，默认 false
}
```

#### 响应

```typescript
interface CleanupResponse {
  success: true;
  data: {
    dryRun: boolean;
    deletedFiles: number;
    freedSpace: string;
    details: {
      http?: { files: number; size: string };
      application?: { files: number; size: string };
      error?: { files: number; size: string };
    };
    completedAt: string;
  };
  message: string;
  timestamp: string;
  path: string;
}
```

#### 示例

**请求**

```bash
curl -X POST 'http://localhost:3001/api/v1/logs/cleanup' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"type": "http", "olderThanDays": 14, "dryRun": true}'
```

**响应**

```json
{
  "success": true,
  "data": {
    "dryRun": true,
    "deletedFiles": 45,
    "freedSpace": "2.3GB",
    "details": {
      "http": { "files": 45, "size": "2.3GB" }
    },
    "completedAt": "2025-12-07T10:40:00.123Z"
  },
  "message": "日志清理完成（试运行）",
  "timestamp": "2025-12-07T10:40:00.000Z",
  "path": "/api/v1/logs/cleanup"
}
```

---

### GET /api/v1/logs/stats

获取日志统计信息。

#### 请求参数（Query）

```typescript
interface QueryLogStatsDto {
  startTime?: string;       // ISO 8601 格式
  endTime?: string;         // ISO 8601 格式（跨度最大 30 天）
  groupBy?: 'hour' | 'day' | 'service' | 'level' | 'region';
}
```

#### 响应

```typescript
interface LogStatsResponse {
  success: true;
  data: {
    period: { start: string; end: string };
    summary: {
      totalLogs: number;
      totalRequests: number;
      errorCount: number;
      warnCount: number;
      avgDuration: number;
      p95Duration: number;
      p99Duration: number;
      slowRequestCount: number;
    };
    byLevel: Record<LogLevel, number>;
    byService: Record<string, number>;
    byRegion: Record<string, number>;
    byHour: { hour: string; count: number; errorRate: number; avgDuration: number }[];
    diskUsage: {
      total: string;
      used: string;
      usedPercent: number;
      byType: Record<string, string>;
    };
  };
  timestamp: string;
  path: string;
}
```

#### 示例

**响应**

```json
{
  "success": true,
  "data": {
    "period": {
      "start": "2025-12-07T00:00:00.000Z",
      "end": "2025-12-07T23:59:59.999Z"
    },
    "summary": {
      "totalLogs": 125000,
      "totalRequests": 50000,
      "errorCount": 150,
      "warnCount": 500,
      "avgDuration": 123,
      "p95Duration": 456,
      "p99Duration": 1234,
      "slowRequestCount": 45
    },
    "byLevel": {
      "ERROR": 150,
      "WARN": 500,
      "INFO": 100000,
      "DEBUG": 24350
    },
    "byService": {
      "IAM": 30000,
      "Form": 50000,
      "Approval": 45000
    },
    "byRegion": {
      "CN": 80000,
      "US": 30000,
      "UAE": 15000
    },
    "byHour": [
      { "hour": "00:00", "count": 5000, "errorRate": 0.1, "avgDuration": 100 },
      { "hour": "01:00", "count": 3000, "errorRate": 0.05, "avgDuration": 90 },
      { "hour": "09:00", "count": 15000, "errorRate": 0.2, "avgDuration": 150 }
    ],
    "diskUsage": {
      "total": "100GB",
      "used": "45GB",
      "usedPercent": 45,
      "byType": {
        "http": "25GB",
        "application": "15GB",
        "error": "5GB"
      }
    }
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/stats"
}
```

---

### GET /api/v1/logs/alerts

获取告警历史列表。

#### 请求参数（Query）

```typescript
interface QueryAlertHistoryDto extends CommonQueryParams {
  type?: AlertType;
  status?: AlertStatus;
}
```

#### 响应

```typescript
interface AlertHistoryResponse {
  success: true;
  data: {
    items: AlertItem[];
    summary: {
      total: number;
      byType: Record<AlertType, number>;
      byStatus: Record<AlertStatus, number>;
    };
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
  timestamp: string;
  path: string;
}
```

#### 示例

**响应**

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "alert-001",
        "type": "SLOW_REQUEST",
        "severity": "WARNING",
        "message": "慢请求告警: POST /api/v1/forms/submit 耗时 5234ms",
        "context": {
          "traceId": "CN-1733580000000-a1b2c3d4",
          "url": "/api/v1/forms/submit",
          "duration": 5234,
          "threshold": 2000
        },
        "sentAt": "2025-12-07T10:30:05.000Z",
        "channels": ["slack", "feishu"],
        "status": "SENT"
      }
    ],
    "summary": {
      "total": 25,
      "byType": {
        "SLOW_REQUEST": 15,
        "HIGH_ERROR_RATE": 5,
        "DISK_SPACE": 3,
        "IP_ANOMALY": 2
      },
      "byStatus": {
        "SENT": 23,
        "FAILED": 2
      }
    },
    "total": 25,
    "page": 1,
    "limit": 50,
    "totalPages": 1,
    "hasNext": false,
    "hasPrev": false
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/alerts"
}
```

---

### GET /api/v1/logs/alerts/config

获取告警配置。

#### 响应

```json
{
  "success": true,
  "data": {
    "slowRequest": {
      "enabled": true,
      "thresholdMs": 2000,
      "excludePaths": ["/api/v1/files/upload", "/api/v1/reports/generate"]
    },
    "errorRate": {
      "enabled": true,
      "thresholdPercent": 5,
      "windowMinutes": 5,
      "minRequests": 100
    },
    "ipAnomaly": {
      "enabled": true,
      "maxRequestsPerMinute": 100,
      "blockDurationMinutes": 10
    },
    "diskSpace": {
      "enabled": true,
      "warningPercent": 80,
      "criticalPercent": 90
    },
    "notifications": {
      "slack": { "enabled": true, "webhookConfigured": true },
      "feishu": { "enabled": true, "webhookConfigured": true },
      "email": { "enabled": false, "recipientCount": 0 }
    },
    "updatedAt": "2025-12-07T10:30:00.123Z",
    "updatedBy": "admin"
  },
  "timestamp": "2025-12-07T10:35:00.000Z",
  "path": "/api/v1/logs/alerts/config"
}
```

---

### PUT /api/v1/logs/alerts/config

更新告警配置。

#### 请求体

```typescript
interface UpdateAlertConfigDto {
  slowRequest?: {
    enabled?: boolean;
    thresholdMs?: number;
    excludePaths?: string[];
  };
  
  errorRate?: {
    enabled?: boolean;
    thresholdPercent?: number;
    windowMinutes?: number;
    minRequests?: number;
  };
  
  ipAnomaly?: {
    enabled?: boolean;
    maxRequestsPerMinute?: number;
    blockDurationMinutes?: number;
  };
  
  diskSpace?: {
    enabled?: boolean;
    warningPercent?: number;
    criticalPercent?: number;
  };
  
  notifications?: {
    slack?: { webhookUrl?: string; enabled?: boolean };
    feishu?: { webhookUrl?: string; enabled?: boolean };
    email?: { recipients?: string[]; enabled?: boolean };
  };
}
```

#### 示例

**请求**

```bash
curl -X PUT 'http://localhost:3001/api/v1/logs/alerts/config' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"slowRequest": {"thresholdMs": 3000}}'
```

**响应**

```json
{
  "success": true,
  "data": {
    "slowRequest": {
      "enabled": true,
      "thresholdMs": 3000
    },
    "notifications": {
      "slack": { "enabled": true },
      "feishu": { "enabled": true }
    },
    "updatedAt": "2025-12-07T10:45:00.123Z",
    "updatedBy": "admin"
  },
  "message": "告警配置已更新",
  "timestamp": "2025-12-07T10:45:00.000Z",
  "path": "/api/v1/logs/alerts/config"
}
```

---

### POST /api/v1/logs/alerts/test

测试告警通道。

#### 请求体

```typescript
interface TestAlertDto {
  channel: 'slack' | 'feishu' | 'email';
  message?: string;  // 自定义测试消息，可选
}
```

#### 响应

```typescript
interface TestAlertResponse {
  success: true;
  data: {
    channel: string;
    status: 'SUCCESS' | 'FAILED';
    sentAt: string;
    errorMessage?: string;  // 仅失败时
  };
  message: string;
  timestamp: string;
  path: string;
}
```

#### 示例

**成功响应**

```json
{
  "success": true,
  "data": {
    "channel": "slack",
    "status": "SUCCESS",
    "sentAt": "2025-12-07T10:50:00.123Z"
  },
  "message": "测试告警已发送",
  "timestamp": "2025-12-07T10:50:00.000Z",
  "path": "/api/v1/logs/alerts/test"
}
```

**失败响应**

```json
{
  "success": false,
  "error": {
    "code": "LOG_ALERT_TEST_FAILED",
    "message": "告警测试发送失败",
    "details": "Slack webhook URL 未配置"
  },
  "timestamp": "2025-12-07T10:50:00.000Z",
  "path": "/api/v1/logs/alerts/test",
  "method": "POST",
  "statusCode": 500
}
```

---

## 错误码

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `UNAUTHORIZED` | 401 | 未认证或 Token 过期 |
| `LOG_PERMISSION_DENIED` | 403 | 权限不足 |
| `LOG_QUERY_INVALID_PARAMS` | 400 | 查询参数无效 |
| `LOG_QUERY_TIME_RANGE_TOO_LARGE` | 400 | 时间范围过大（最大 7 天） |
| `LOG_QUERY_TOO_BROAD` | 400 | 查询范围过大，请添加过滤条件 |
| `LOG_TRACE_NOT_FOUND` | 404 | 追踪 ID 未找到 |
| `LOG_CONFIG_UPDATE_FAILED` | 500 | 配置更新失败 |
| `LOG_CLEANUP_IN_PROGRESS` | 409 | 清理任务进行中 |
| `LOG_ALERT_CHANNEL_INVALID` | 400 | 告警通道配置无效 |
| `LOG_ALERT_TEST_FAILED` | 500 | 告警测试发送失败 |
| `INTERNAL_SERVER_ERROR` | 500 | 未预期的服务器错误 |

---

## 使用示例

### 查询特定用户的错误日志

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/errors?userId=user-001&startTime=2025-12-07T00:00:00Z' \
  -H 'Authorization: Bearer <token>'
```

### 追踪一个完整请求链路

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/trace/CN-1733580000000-a1b2c3d4' \
  -H 'Authorization: Bearer <token>'
```

### 查看慢请求统计（阈值 3 秒）

```bash
curl -X GET 'http://localhost:3001/api/v1/logs/slow-requests?thresholdMs=3000&service=Form' \
  -H 'Authorization: Bearer <token>'
```

### 动态调整日志级别

```bash
curl -X PUT 'http://localhost:3001/api/v1/logs/config' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"level": "DEBUG"}'
```

### 手动清理 14 天前的 HTTP 日志（试运行）

```bash
curl -X POST 'http://localhost:3001/api/v1/logs/cleanup' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"type": "http", "olderThanDays": 14, "dryRun": true}'
```

### 更新慢请求告警阈值

```bash
curl -X PUT 'http://localhost:3001/api/v1/logs/alerts/config' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"slowRequest": {"thresholdMs": 3000}}'
```

### 测试 Slack 告警通道

```bash
curl -X POST 'http://localhost:3001/api/v1/logs/alerts/test' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"channel": "slack", "message": "测试告警消息"}'
```

---

## 数据模型

### LogService 推荐枚举

> **说明**: `service` 字段为字符串类型，以下为推荐值。新服务可按需扩展，但建议使用统一的 PascalCase 命名。

```typescript
// 推荐服务枚举（非强制，可扩展）
type LogService = 
  // 核心服务
  | 'IAM'           // 身份认证服务
  | 'Form'          // 表单引擎
  | 'Approval'      // 审批引擎
  | 'Inventory'     // 库存管理
  
  // 业务服务
  | 'HR'            // 人事管理
  | 'Finance'       // 财务管理
  | 'Report'        // 报表服务
  | 'Notification'  // 通知服务
  
  // 基础设施
  | 'Gateway'       // API 网关
  | 'Scheduler'     // 定时任务
  | 'FileStorage'   // 文件存储
  | 'Temporal'      // 工作流引擎
  
  // 其他服务（按需扩展）
  | string;         // 允许自定义服务名
```

### LogLevel 枚举

```typescript
enum LogLevel {
  ERROR = 'ERROR',   // 错误：系统异常、业务错误
  WARN = 'WARN',     // 警告：潜在问题、性能隐患
  INFO = 'INFO',     // 信息：正常业务日志
  DEBUG = 'DEBUG'    // 调试：详细调试信息
}
```

### AlertType 枚举

```typescript
enum AlertType {
  SLOW_REQUEST = 'SLOW_REQUEST',       // 慢请求告警
  HIGH_ERROR_RATE = 'HIGH_ERROR_RATE', // 高错误率告警
  DISK_SPACE = 'DISK_SPACE',           // 磁盘空间告警
  IP_ANOMALY = 'IP_ANOMALY'            // IP 异常告警
}
```

### AlertStatus 枚举

```typescript
enum AlertStatus {
  SENT = 'SENT',               // 已发送
  FAILED = 'FAILED',           // 发送失败
  ACKNOWLEDGED = 'ACKNOWLEDGED' // 已确认
}
```

### AlertSeverity 枚举

```typescript
enum AlertSeverity {
  CRITICAL = 'CRITICAL',  // 严重：需要立即处理
  WARNING = 'WARNING',    // 警告：需要关注
  INFO = 'INFO'           // 信息：仅供参考
}
```

---

## 相关文档

- [日志系统 PRD](PRD.md) - 产品需求文档
- [日志系统架构](ARCHITECTURE.md) - 架构设计
- [日志系统 TODO](TODO.md) - 开发待办
- [API 请求响应规范](../../../../../docs/standards/API_STANDARDS.md) - 项目 API 统一规范

---

**文档维护**: FFOA 开发团队  
**更新频率**: API 变更时更新
