# 日志系统 PRD

**版本**: v1.2.0  
**状态**: ✅ 所有功能已完成  
**最后更新**: 2025-12-15

---

## 背景与目标

### 背景

FFOA 作为企业级办公自动化平台，需要一套完整的日志系统来支撑：

1. **开发调试** - 快速定位 API 问题和业务逻辑错误
2. **运维监控** - 实时监控系统运行状态和性能指标
3. **故障排查** - 通过日志追踪完整请求链路，快速定位问题根源
4. **安全监控** - 记录异常访问和潜在安全威胁
5. **多区域运维** - 支持 US / CN / UAE 多区域部署的日志关联
6. **微服务追踪** - 跨 IAM / Form / Approval / Inventory 等服务的请求追踪

### 目标

| 目标 | 描述 | 衡量指标 |
|------|------|----------|
| **可观测性** | 系统运行状态全面可见 | 100% API 请求可追踪 |
| **快速定位** | 问题快速发现和定位 | 平均故障定位时间 < 10min |
| **低侵入** | 不影响业务代码和性能 | 日志开销 < 5ms/请求 |
| **自动化** | 日志自动轮转和清理 | 无需人工干预 |
| **高可用** | 日志系统故障不影响业务 | Fallback 机制 100% 覆盖 |
| **可扩展** | 支持大规模用户 | 10K+ 用户日志量可控 |

---

## 边界声明

### 本模块职责

| 职责 | 说明 |
|------|------|
| HTTP 请求/响应日志 | 记录所有 API 调用的详细信息 |
| 应用运行日志 | 记录应用启动、服务状态等信息 |
| 错误日志 | 记录异常和错误堆栈（含详细错误响应） |
| 分布式追踪 | traceId/spanId 跨服务追踪 |
| Temporal 活动日志 | 工作流 Activity 执行日志 |
| 日志轮转与采样 | 按日期轮转，大流量采样 |
| 敏感数据脱敏 | 自动脱敏密码、token 等敏感字段 |
| 异常告警 | 慢请求、高错误率告警 |

### 非本模块职责（由审计系统负责）

| 职责 | 归属模块 |
|------|----------|
| 业务操作审计 | [审计系统](../../../audit/docs/PRD.md) |
| 合规性记录 | [审计系统](../../../audit/docs/PRD.md) |
| 不可篡改的操作记录 | [审计系统](../../../audit/docs/PRD.md) |
| 变更历史追溯 | [审计系统](../../../audit/docs/PRD.md) |
| SOX 合规 | [审计系统](../../../audit/docs/PRD.md) |

---

## 功能需求

> **版本状态说明**:
> - ✅ 所有功能已在 v1.2.0 完成并部署
> - v1.0: 基础日志功能（2025-11-16）
> - v1.1: 错误日志增强（2025-11-26）
> - v1.2: 分布式追踪、采样、告警、Temporal 日志（2025-12-14）

### F1: HTTP 请求日志（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F1.1 请求基本信息 | P0 | ✅ | HTTP 方法、URL、用户、IP |
| F1.2 请求参数记录 | P0 | ✅ | Body、Query、Params（debug 模式） |
| F1.3 响应状态记录 | P0 | ✅ | 状态码、响应时间 |
| F1.4 响应数据记录 | P1 | ✅ | 响应内容（debug 模式，自动截断） |
| F1.5 唯一请求 ID | P0 | ✅ | 每个请求分配唯一 RequestID |
| F1.6 用户信息关联 | P0 | ✅ | 记录 userId、username |
| F1.7 客户端信息 | P1 | ✅ | IP 地址、User-Agent |

### F2: 日志输出（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F2.1 控制台输出 | P0 | ✅ | 实时彩色输出（带表情符号） |
| F2.2 文件输出 | P0 | ✅ | 持久化到文件 |
| F2.3 分类输出 | P1 | ✅ | application/error/http 分类 |
| F2.4 JSON 结构化输出 | P1 | ✅ | ELK/Cloud Logging 兼容格式 |

### F3: 日志管理（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F3.1 日期轮转 | P0 | ✅ | 按日期自动创建新文件 |
| F3.2 自动压缩 | P1 | ✅ | 旧日志自动压缩为 .gz |
| F3.3 自动清理 | P1 | ✅ | 超期日志自动删除 |
| F3.4 大小限制 | P1 | ✅ | 单文件最大 20-50MB |

### F4: 安全与隐私（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F4.1 敏感字段脱敏 | P0 | ✅ | password、token 等自动脱敏 |
| F4.2 响应数据截断 | P1 | ✅ | 防止日志过大（超 1000 字符截断） |
| F4.3 递归脱敏 | P1 | ✅ | 嵌套对象也脱敏 |

### F5: 错误日志增强（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F5.1 完整错误响应 | P0 | ✅ | 记录 HttpException 的完整 response |
| F5.2 错误堆栈 | P0 | ✅ | 开发环境记录完整堆栈 |
| F5.3 自定义字段保留 | P1 | ✅ | 保留业务异常中的自定义字段 |

### F6: 分布式追踪 Log Correlation（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F6.1 traceId | P0 | ✅ | 跨服务请求追踪 ID |
| F6.2 spanId | P1 | ✅ | 当前操作 ID |
| F6.3 parentSpanId | P1 | ✅ | 父操作 ID（调用链） |
| F6.4 region 标识 | P1 | ✅ | US / CN / UAE 区域标识 |
| F6.5 service 标识 | P0 | ✅ | IAM / Form / Approval 等服务标识 |

### F7: 告警机制（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F7.1 慢请求告警 | P0 | ✅ | 请求时间 > 2s（可配置） |
| F7.2 高错误率告警 | P0 | ✅ | 错误率超过阈值 |
| F7.3 IP 异常告警 | P1 | ✅ | 单 IP 请求频率异常 |
| F7.4 磁盘空间告警 | P1 | ✅ | 日志磁盘空间不足 |

### F8: 日志采样 Log Sampling（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F8.1 Debug 日志采样 | P1 | ✅ | 大流量时采样记录 |
| F8.2 HTTP 日志采样 | P1 | ✅ | 高 QPS 时按比例采样 |
| F8.3 错误日志全量 | P0 | ✅ | 错误日志永远 100% 记录 |

### F9: 写入失败容错（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F9.1 Fallback to Console | P0 | ✅ | 文件写入失败时回退到控制台 |
| F9.2 告警通知 | P1 | ✅ | 连续失败时发送告警 |
| F9.3 自动恢复 | P1 | ✅ | 问题修复后自动恢复文件写入 |

### F10: Temporal 工作流日志（已实现）

| 功能点 | 优先级 | 状态 | 说明 |
|--------|--------|------|------|
| F10.1 Activity 日志 | P0 | ✅ | 记录 Activity 执行开始/结束 |
| F10.2 Workflow 状态日志 | P1 | ✅ | 记录 Workflow 状态变化 |
| F10.3 重试日志 | P1 | ✅ | 记录 Activity 重试信息 |

---

## ID 命名约定

> **重要**: 本节定义了日志系统中各类 ID 的命名规范和使用场景。

### ID 类型对比

| ID 类型 | 格式示例 | 作用域 | 使用场景 |
|---------|----------|--------|----------|
| **RequestID** | `1733580000123-abc` | 单服务 | 服务内部请求追踪，日志 grep |
| **traceId** | `CN-1733580000000-a1b2c3d4` | 跨服务 | 跨区域、跨服务请求关联 |
| **spanId** | `FORM-1733580000123-x1y2z3` | 单操作 | 调用链中的单个操作标识 |

### 使用指南

| 排查场景 | 推荐使用 | 原因 |
|----------|----------|------|
| 单服务单点问题 | **RequestID** | 格式简单，grep 效率高 |
| 跨服务调用链追踪 | **traceId** | 包含 region，可关联多服务 |
| 调用链深度分析 | **traceId + spanId** | 可还原完整调用树 |

### 生成规则

```typescript
// RequestID: 简单时间戳 + 随机串（单服务内唯一）
const requestId = `${Date.now()}-${nanoid(6)}`;
// 示例: 1733580000123-abc

// traceId: 包含 region 信息（跨服务传递）
const traceId = `${region}-${Date.now()}-${nanoid(8)}`;
// 示例: CN-1733580000000-a1b2c3d4

// spanId: 包含 service 信息（标识当前操作）
const spanId = `${service}-${Date.now()}-${nanoid(6)}`;
// 示例: FORM-1733580000123-x1y2z3
```

### Header 传递规范

| Header | 方向 | 说明 |
|--------|------|------|
| `X-Request-Id` | 响应 | 返回给客户端，便于用户反馈问题 |
| `X-Trace-Id` | 请求/响应 | 跨服务传递，入口服务生成 |
| `X-Span-Id` | 请求 | 下游服务接收，作为 parentSpanId |

### OpenTelemetry 兼容性

> 当前 `traceId` / `spanId` / `parentSpanId` 设计与 **OpenTelemetry** 概念兼容。后续可通过 OTel SDK 将日志与分布式链路追踪系统（如 Jaeger / Tempo / Zipkin）打通，实现日志 ↔ 链路追踪 ↔ 指标的完整可观测性。

---

## 分布式追踪设计 (Log Correlation)

### 追踪字段定义

```typescript
interface TraceContext {
  traceId: string;       // 全局追踪 ID（跨服务）
  spanId: string;        // 当前操作 ID
  parentSpanId?: string; // 父操作 ID（调用链）
  
  region: string;        // 区域: US | CN | UAE
  service: string;       // 服务: IAM | Form | Approval | Inventory
  instance: string;      // 实例 ID
  
  workflowId?: string;   // Temporal Workflow ID
  activityId?: string;   // Temporal Activity ID
}
```

### 追踪 ID 生成规则

```typescript
// traceId 格式: {region}-{timestamp}-{random}
// 示例: CN-1733580000000-a1b2c3d4

// spanId 格式: {service}-{timestamp}-{random}
// 示例: FORM-1733580000123-x1y2z3

// Header 传递
// X-Trace-Id: CN-1733580000000-a1b2c3d4
// X-Span-Id: FORM-1733580000123-x1y2z3
// X-Parent-Span-Id: IAM-1733580000100-m1n2o3
```

### 跨服务调用示例

```
[用户请求] → [IAM 服务] → [Form 服务] → [Approval 服务]
     │            │            │              │
     └─ traceId ──┴─ traceId ──┴── traceId ───┘
                  │            │              │
              spanId-1    spanId-2       spanId-3
                  │            │              │
                  └─ parent ───┴── parent ────┘
```

---

## 结构化 JSON 日志规范 (ELK 兼容)

### JSON 日志字段标准

```typescript
interface StructuredLog {
  // 基础字段
  "@timestamp": string;        // ISO 8601 格式
  "level": string;             // ERROR | WARN | INFO | DEBUG
  "message": string;           // 日志消息
  
  // 追踪字段
  "trace": {
    "id": string;              // traceId
    "span_id": string;         // spanId
    "parent_span_id"?: string; // parentSpanId
  };
  
  // 请求字段
  "http": {
    "request_id": string;      // RequestID
    "method": string;          // GET | POST | PUT | DELETE
    "url": string;             // 请求 URL
    "status_code": number;     // 响应状态码
    "duration_ms": number;     // 响应时间
  };
  
  // 用户字段
  "user": {
    "id": string;              // userId
    "name": string;            // username
  };
  
  // 客户端字段
  "client": {
    "ip": string;              // IP 地址
    "user_agent": string;      // User-Agent
    "geo"?: {
      "country": string;
      "city": string;
    };
  };
  
  // 服务字段
  "service": {
    "name": string;            // IAM | Form | Approval
    "version": string;         // 服务版本
    "instance": string;        // 实例 ID
    "region": string;          // US | CN | UAE
  };
  
  // Temporal 字段（可选）
  "temporal"?: {
    "workflow_id": string;
    "workflow_type": string;
    "activity_id"?: string;
    "activity_type"?: string;
    "attempt"?: number;
  };
  
  // 错误字段（可选）
  "error"?: {
    "type": string;            // 错误类型
    "message": string;         // 错误消息
    "code"?: string;           // 错误代码
    "stack"?: string;          // 堆栈信息（仅开发环境）
    "details"?: object;        // 详细信息
  };
}
```

### JSON 日志示例

```json
{
  "@timestamp": "2025-12-07T10:30:00.123Z",
  "level": "INFO",
  "message": "POST /api/v1/forms | 201 | 234ms",
  "trace": {
    "id": "CN-1733580000000-a1b2c3d4",
    "span_id": "FORM-1733580000123-x1y2z3",
    "parent_span_id": "IAM-1733580000100-m1n2o3"
  },
  "http": {
    "request_id": "1733580000123-abc",
    "method": "POST",
    "url": "/api/v1/forms",
    "status_code": 201,
    "duration_ms": 234
  },
  "user": {
    "id": "user-001",
    "name": "zhangsan"
  },
  "client": {
    "ip": "192.168.1.100",
    "user_agent": "Mozilla/5.0..."
  },
  "service": {
    "name": "Form",
    "version": "1.0.0",
    "instance": "form-service-pod-1",
    "region": "CN"
  }
}
```

---

## 告警机制设计

> **配置归属说明**:
> - `AlertConfig` 由**平台运维团队**统一维护
> - 推荐以 `config/logging.alerts.yaml` 或集中配置中心（如 Apollo / Nacos）存储
> - 各服务在启动时加载配置，业务开发一般无需修改告警规则

### 告警规则配置

```typescript
interface AlertConfig {
  slowRequest: {
    enabled: boolean;
    thresholdMs: number;      // 默认 2000ms
    excludePaths: string[];   // 排除的路径（如文件上传）
  };
  
  errorRate: {
    enabled: boolean;
    thresholdPercent: number; // 默认 5%
    windowMinutes: number;    // 统计窗口，默认 5 分钟
    minRequests: number;      // 最小请求数，默认 100
  };
  
  ipAnomaly: {
    enabled: boolean;
    maxRequestsPerMinute: number; // 默认 100
    blockDurationMinutes: number; // 阻止时长，默认 10 分钟
  };
  
  diskSpace: {
    enabled: boolean;
    warningPercent: number;   // 警告阈值，默认 80%
    criticalPercent: number;  // 严重阈值，默认 90%
  };
}
```

### 告警通知渠道

| 渠道 | 优先级 | 适用场景 |
|------|--------|----------|
| Slack/飞书 | P0 | 实时告警 |
| Email | P1 | 日报/周报 |
| PM2 日志 | P2 | 本地开发 |

### 告警示例

```
🚨 [SLOW_REQUEST] CN Region
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Path: POST /api/v1/forms/submit
Duration: 5234ms (threshold: 2000ms)
User: zhangsan (user-001)
TraceId: CN-1733580000000-a1b2c3d4
Time: 2025-12-07 10:30:00 CST
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

---

## 日志采样策略 (Log Sampling)

> **配置归属说明**:
> - `SamplingConfig` 由**平台运维团队**统一配置
> - 业务服务一般**不应修改**采样规则，避免关键日志丢失
> - 如有特殊需求（如某接口需要 100% 日志），请联系平台团队调整

### 采样规则

| 日志类型 | 采样条件 | 采样率 |
|----------|----------|--------|
| 错误日志 | 永远 | 100% |
| 慢请求日志 | duration > 1s | 100% |
| 认证失败 | 永远 | 100% |
| DEBUG 日志 | QPS > 1000 | 10% |
| HTTP 成功日志 | QPS > 5000 | 20% |
| 健康检查 | 永远 | 0%（不记录） |

### 采样配置

```typescript
interface SamplingConfig {
  enabled: boolean;
  
  rules: {
    // 永远 100% 记录
    alwaysLog: {
      levels: ['ERROR', 'WARN'];
      paths: ['/api/v1/auth/*'];  // 认证相关
      conditions: ['duration > 1000'];
    };
    
    // 永远不记录
    neverLog: {
      paths: ['/health', '/ready', '/metrics'];
    };
    
    // 动态采样
    dynamic: {
      qpsThreshold: 1000;        // QPS 超过此值开始采样
      minSampleRate: 0.1;        // 最低采样率 10%
      maxSampleRate: 1.0;        // 最高采样率 100%
    };
  };
}
```

---

## 写入失败容错策略

### Fallback 机制

```
文件写入
    │
    ├─ 成功 → 正常记录
    │
    └─ 失败 ┬─ Fallback 到 Console
            │
            ├─ 连续失败 3 次 → 发送告警
            │
            └─ 每 5 分钟重试恢复文件写入
```

### 失败原因处理

| 失败原因 | 处理策略 |
|----------|----------|
| 磁盘满 | 紧急删除最旧日志，发送告警 |
| 文件句柄超限 | 回退到 Console，发送告警 |
| 权限错误 | 回退到 Console，发送告警 |
| 网络存储故障 | 本地缓存 + 延迟写入 |

### 自动清理策略

```typescript
interface CleanupConfig {
  // 紧急清理（磁盘 > 90%）
  emergency: {
    enabled: boolean;
    triggerPercent: 90;
    action: 'DELETE_OLDEST';  // 删除最旧的日志
    targetPercent: 70;        // 清理到 70%
  };
  
  // 常规清理（每日凌晨 3 点）
  scheduled: {
    enabled: boolean;
    cronExpression: '0 3 * * *';
    retentionDays: {
      http: 14;
      application: 30;
      error: 30;
    };
  };
}
```

---

## 多区域日志目录结构

> **环境差异说明**:
> - **开发环境**: 默认使用 `backend/logs/` 本地目录，单服务结构
> - **生产环境**: 推荐使用 `/var/log/ffoa/{REGION}/{SERVICE}/` 结构，由 DevOps 在部署脚本中配置
> - 具体路径通过环境变量 `LOG_BASE_DIR` 控制，业务代码无需关心

### 目录结构（生产环境）

```
/var/log/ffoa/
├── CN/                           # 中国区域
│   ├── iam/                      # IAM 服务
│   │   ├── application-2025-12-07.log
│   │   ├── error-2025-12-07.log
│   │   └── http/
│   │       └── http-2025-12-07.log
│   ├── form/                     # Form 服务
│   ├── approval/                 # Approval 服务
│   └── temporal/                 # Temporal 工作流
│       ├── workflow-2025-12-07.log
│       └── activity-2025-12-07.log
│
├── US/                           # 美国区域
│   └── ...
│
└── UAE/                          # 阿联酋区域
    └── ...
```

### 环境变量配置

```bash
# 区域配置
LOG_REGION=CN                     # US | CN | UAE
LOG_SERVICE=Form                  # IAM | Form | Approval | Inventory
LOG_INSTANCE=form-pod-1           # Pod/实例名称

# 目录配置
LOG_BASE_DIR=/var/log/ffoa
LOG_TEMPORAL_DIR=/var/log/ffoa/${LOG_REGION}/temporal
```

---

## Temporal 工作流日志

> **灵活性说明**: Temporal 日志字段为推荐标准，具体实现可由「流程引擎 / 审批团队」在不破坏核心字段（`workflow_id`、`activity_type`、`status`）的前提下按实际情况扩展。

### Activity 日志格式

```typescript
interface ActivityLog {
  "@timestamp": string;
  "level": string;
  "message": string;
  
  "temporal": {
    // 核心字段（必需）
    "workflow_id": string;
    "workflow_type": string;      // ApprovalWorkflow | FormWorkflow
    "workflow_run_id": string;
    
    "activity_id": string;
    "activity_type": string;      // SendNotification | UpdateStatus
    
    // 扩展字段（推荐）
    "attempt": number;            // 重试次数
    "start_to_close_timeout": number;
    "schedule_to_start_timeout": number;
  };
  
  "execution": {
    "status": string;             // STARTED | COMPLETED | FAILED | RETRYING（核心）
    "duration_ms"?: number;
    "error"?: {
      "type": string;
      "message": string;
    };
  };
  
  "context": {
    "business_key"?: string;      // 业务主键
    "initiator_id"?: string;      // 发起人
  };
}
```

### Workflow 状态日志

```json
{
  "@timestamp": "2025-12-07T10:30:00.123Z",
  "level": "INFO",
  "message": "Workflow state changed: PENDING → IN_PROGRESS",
  "temporal": {
    "workflow_id": "approval-form-001",
    "workflow_type": "ApprovalWorkflow",
    "workflow_run_id": "run-xyz-123"
  },
  "execution": {
    "previous_state": "PENDING",
    "current_state": "IN_PROGRESS",
    "trigger": "USER_SUBMIT"
  },
  "context": {
    "business_key": "form-instance-001",
    "initiator_id": "user-001"
  }
}
```

---

## 日志格式（当前实现）

### 请求日志

```
📥 [RequestID] METHOD URL | User: username(userId) | IP: xxx.xxx.xxx.xxx
```

### 成功响应

```
✅ [RequestID] METHOD URL | StatusCode | ResponseTime | User: username(userId)
```

### 错误响应

```
❌ [RequestID] METHOD URL | StatusCode | ResponseTime | User: username(userId)
[RequestID] Error: ErrorName - ErrorMessage
[RequestID] Error Details: {"message":"详细错误","error":"ERROR_CODE","customField":"value"}
```

---

## 文件结构（当前实现）

```
backend/logs/
├── application-YYYY-MM-DD.log    # 所有日志
├── error-YYYY-MM-DD.log          # 仅错误
└── http/
    └── http-YYYY-MM-DD.log       # HTTP 请求/响应
```

### 保留策略

| 日志类型 | 保留时间 | 单文件大小 |
|----------|----------|------------|
| application | 30 天 | 20MB |
| error | 30 天 | 20MB |
| http | 14 天 | 50MB |

---

## 配置说明

### 环境变量

| 变量 | 值 | 说明 |
|------|-----|------|
| `NODE_ENV` | `development`/`production` | 应用环境 |
| `LOG_LEVEL` | `error`/`warn`/`info`/`debug` | 日志级别 |
| `LOG_REGION` | `CN`/`US`/`UAE` | 区域标识 |
| `LOG_SERVICE` | `IAM`/`Form`/`Approval` | 服务标识 |
| `LOG_SAMPLING_ENABLED` | `true`/`false` | 是否启用采样 |
| `LOG_ALERT_SLOW_THRESHOLD_MS` | `2000` | 慢请求阈值（毫秒） |

### 日志级别说明

| 级别 | 请求信息 | Body/Query | 响应数据 | 错误堆栈 | 适用场景 |
|------|---------|-----------|---------|---------|---------|
| `error` | ❌ | ❌ | ❌ | ✅ | 生产监控 |
| `info` | ✅ | ❌ | ❌ | ❌ | 生产环境 |
| `debug` | ✅ | ✅ | ✅ | ✅ | 开发调试 |

---

## 技术实现

### 技术栈

| 组件 | 技术 | 用途 |
|------|------|------|
| 日志库 | Winston | 多输出方式支持 |
| 日志轮转 | winston-daily-rotate-file | 自动轮转 |
| 请求拦截 | NestJS Interceptor | 自动记录 HTTP 日志 |
| 追踪 ID | uuid / nanoid | 生成 traceId/spanId |

### 核心文件

```
backend/src/modules/logging/
├── docs/                           # 模块文档
│   ├── README.md
│   ├── PRD.md
│   ├── ARCHITECTURE.md
│   ├── API.md
│   └── TODO.md
├── config/
│   └── winston.config.ts           # Winston 配置
├── services/
│   ├── logger.service.ts           # NestJS Logger 服务
│   ├── trace.context.ts            # 追踪上下文（规划）
│   └── sampling.service.ts         # 采样服务（规划）
├── interceptors/
│   └── logging.interceptor.ts      # HTTP 日志拦截器
├── alerts/
│   └── alert.service.ts            # 告警服务（规划）
├── logging.module.ts               # 日志模块
└── index.ts                        # 模块导出
```

---

## 日志分析

### 常用命令

```bash
# 实时查看 HTTP 日志
tail -f logs/http/http-$(date +%Y-%m-%d).log

# 通过 traceId 追踪（跨服务）
grep "CN-1733580000000-a1b2c3d4" /var/log/ffoa/CN/*/http/*.log

# 查找慢请求（>2秒）
grep "ms" logs/http/http-$(date +%Y-%m-%d).log | \
  awk -F'|' '$3 ~ /[0-9]+ms/ {split($3, a, "ms"); if(a[1] > 2000) print}'

# 统计各区域请求数
for region in CN US UAE; do
  echo "$region: $(grep -c '📥' /var/log/ffoa/$region/*/http/*.log 2>/dev/null || echo 0)"
done

# Temporal Activity 失败统计
grep '"status":"FAILED"' /var/log/ffoa/CN/temporal/activity-*.log | wc -l
```

---

## 验收标准

### 已完成 ✅

- [x] HTTP 请求自动记录完整信息
- [x] 错误请求包含堆栈信息和详细错误响应
- [x] 敏感字段已脱敏
- [x] 日志按日期自动轮转
- [x] 旧日志自动压缩
- [x] 分布式追踪（traceId/spanId）
- [x] 结构化 JSON 日志（ELK 兼容）
- [x] 慢请求/高错误率告警
- [x] 日志采样机制
- [x] 写入失败 Fallback
- [x] 多区域日志结构
- [x] Temporal Activity 日志
- [x] 数据库持久化存储
- [x] 日志管理 API

---

## 里程碑

| 阶段 | 内容 | 状态 | 完成日期 |
|------|------|------|----------|
| Phase 1 | 基础 HTTP 日志记录 | ✅ 已完成 | 2025-11-16 |
| Phase 2 | 文件输出和轮转 | ✅ 已完成 | 2025-11-16 |
| Phase 3 | 敏感数据脱敏 | ✅ 已完成 | 2025-11-16 |
| Phase 4 | 分布式追踪 | ✅ 已完成 | 2025-12-14 |
| Phase 5 | JSON 结构化输出 | ✅ 已完成 | 2025-12-14 |
| Phase 6 | 告警机制 | ✅ 已完成 | 2025-12-14 |
| Phase 7 | 日志采样 | ✅ 已完成 | 2025-12-14 |
| Phase 8 | 写入容错 | ✅ 已完成 | 2025-12-14 |
| Phase 9 | 多区域支持 | ✅ 已完成 | 2025-12-14 |
| Phase 10 | Temporal 日志 | ✅ 已完成 | 2025-12-14 |
| Phase 11 | 日志管理 API | ✅ 已完成 | 2025-12-14 |

---

## 开发使用指南

> 本节面向业务开发人员，说明如何在 Controller / Service 中正确使用日志。

### Logger 命名规范

| 类名 | 职责 | 使用场景 |
|------|------|----------|
| `AppLogger` | 对业务暴露的日志服务 | **业务 Controller / Service 中推荐使用** |
| `LoggerService` | 框架内核心实现 | 仅框架服务（Interceptor、AlertService）使用 |
| `Logger`（NestJS 原生） | 框架自身日志 | `main.ts` bootstrap、模块初始化 |

### 1. 注入 Logger

```typescript
// ✅ 推荐：业务代码统一使用 AppLogger
import { AppLogger } from '@/modules/logging';

@Injectable()
export class FormService {
  constructor(private readonly logger: AppLogger) {}
}

// ⚠️ 仅在无法注入时使用 NestJS 原生 Logger（如静态方法、工具类）
import { Logger } from '@nestjs/common';
const logger = new Logger('MyUtility');
```

### 2. 日志级别使用规范

| 级别 | 使用场景 | 示例 |
|------|----------|------|
| `error` | 需要立即关注的错误 | 数据库连接失败、外部 API 错误 |
| `warn` | 潜在问题但不影响功能 | 重试成功、降级处理 |
| `info` | 关键业务节点 | 表单提交成功、审批通过 |
| `debug` | 调试信息 | 中间变量、详细参数 |

### 3. 推荐打点模式

```typescript
@Injectable()
export class FormService {
  constructor(private readonly logger: AppLogger) {}

  async submitForm(dto: SubmitFormDto, user: UserContext) {
    // 创建带上下文的 logger
    const log = this.logger.withContext({
      module: 'FormSubmit',
      userId: user.id,
      formId: dto.formId,
    });
    
    // 开始操作
    log.info('Start submit form');
    
    try {
      // 业务逻辑...
      const result = await this.doSubmit(dto);
      
      // 成功日志（包含关键结果）
      log.info('Form submitted successfully', { 
        instanceId: result.id,
        status: result.status,
      });
      
      return result;
    } catch (error) {
      // 错误日志（包含完整上下文）
      log.error('Form submit failed', { 
        error: error.message,
        stack: error.stack,
        dto,  // 便于问题复现
      });
      throw error;
    }
  }
}
```

### 4. 常见场景模板

```typescript
// ✅ 外部 API 调用
log.info('Calling external API', { endpoint, timeout });
try {
  const result = await this.httpService.post(endpoint, data);
  log.info('External API success', { statusCode: result.status });
} catch (error) {
  log.error('External API failed', { endpoint, error: error.message });
}

// ✅ 数据库操作（批量）
log.info('Batch update starting', { count: items.length });
const result = await this.prisma.user.updateMany({ ... });
log.info('Batch update completed', { affected: result.count });

// ✅ 异步任务
log.info('Async task queued', { taskId, type: 'email' });
// ... 任务完成后
log.info('Async task completed', { taskId, duration: Date.now() - start });

// ✅ 重试场景
log.warn('Operation failed, retrying', { attempt: 2, maxAttempts: 3 });
```

### 5. 不要这样做

```typescript
// ❌ 日志信息不完整
log.error('Error occurred');  // 没有任何上下文

// ❌ 敏感信息泄露（系统会自动脱敏，但最好不要传入）
log.info('User login', { password: dto.password });

// ❌ 过度日志（生产环境影响性能）
for (const item of items) {
  log.debug('Processing item', { item });  // 大循环内不要打 debug
}

// ❌ 日志级别错误
log.error('User not found');  // 这应该是 warn 或 info，不是 error

// ❌ 手动生成/修改追踪 ID（会导致链路断裂）
log.info('Processing', { traceId: 'my-custom-trace-id' });  // 不要这样做！
```

### 6. 追踪 ID 使用约定

> ✅ **重要约定**: `RequestID` / `traceId` / `spanId` 由**系统层统一生成和注入**（通过 Interceptor / Middleware），业务代码**只读取不修改**，避免跨服务链路断裂。

```typescript
// ✅ 正确：从请求上下文读取 traceId（如需记录到业务日志）
const traceId = request.headers['x-trace-id'];
log.info('Processing request', { traceId });  // 读取，不是自己生成

// ✅ 正确：使用 logger 的 withContext，框架自动注入追踪信息
const log = this.logger.withContext({ module: 'FormSubmit' });
// traceId/spanId 由框架自动附加，无需手动传入

// ❌ 错误：手动生成追踪 ID
const myTraceId = `MY-${Date.now()}`;  // 不要这样做！
```

---

## 与审计系统的关系

| 维度 | 日志系统 | 审计系统 |
|------|----------|----------|
| **定位** | 技术运维 | 业务合规 |
| **目标** | 调试、监控、问题排查 | 操作追溯、合规存证 |
| **内容** | HTTP 请求/响应、错误 | 业务操作、数据变更 |
| **保留期** | 14-30 天 | 3-7 年 |
| **可修改** | 可清理 | 不可删除、不可修改 |
| **合规要求** | 无特殊要求 | SOX 合规 |

---

## 相关文档

- [ARCHITECTURE.md](ARCHITECTURE.md) - 架构设计文档
- [API.md](API.md) - API 接口文档
- [TODO.md](TODO.md) - 开发待办
- [审计系统 PRD](../../../audit/docs/PRD.md) - 业务合规审计

---

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