新增后端日志,且更改了平台名字

This commit is contained in:
wangjiming
2026-08-19 17:26:49 +08:00
parent c3e96ae61b
commit 78680a5c5c
18 changed files with 865 additions and 538 deletions

View File

@@ -1,412 +1,489 @@
# 生产级日志系统设计方案
> 版本v1.0
> 日期2026-08-17
> 状态:待评审
好的,这是一份可以直接放在项目根目录的 `日志规范要求.md`,涵盖**格式标准、分类分级、内容规范、链路追踪、性能安全、运维告警**六大模块,每条规范都配有正反例,你的团队照着这个写代码就行。
---
## 一、现状分析
# 生产级日志规范要求
### 1.1 当前日志架构
```
┌─────────────┐
│ FastAPI │ ← 请求入口
└──────┬──────┘
┌─────────────┐
│ Logging │ ← Python logging 模块
│ Middleware │
└──────┬──────┘
├──────────────────┬──────────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Console │ │ File │ ← 输出目标
│ (开发环境) │ │ (JSON格式) │
└─────────────┘ └─────────────┘
┌─────────────┐
│ audit_logs │ ← 审计日志表
│ (PostgreSQL) │
└─────────────┘
```
### 1.2 现有组件
| 组件 | 文件路径 | 功能 |
|------|----------|------|
| `logging.py` | `backend/app/core/` | 日志配置、JSON 格式化、按日期/大小轮转 |
| `platform_store.py` | `backend/app/db/` | `record_audit()` 审计日志写入 |
| `002_governance.sql` | `backend/app/db/sql/` | `audit_logs` 表结构 |
### 1.3 存在的问题
| 问题 | 影响 | 严重程度 |
|------|------|----------|
| **无结构化日志分级** | DEBUG/INFO/WARNING/ERROR 全部混在一起,无法按级别过滤查看 | 🔴 高 |
| **无请求链路追踪** | 一个请求从进入到返回经过哪些服务/函数,无法串联 | 🔴 高 |
| **审计日志与业务耦合** | 各模块手动调用 `record_audit()`,容易遗漏 | 🟡 中 |
| **无敏感数据脱敏** | 用户 token、密码等可能明文记录 | 🔴 高 |
| **无日志聚合查询** | 无法按用户/时间范围/操作类型快速检索 | 🟡 中 |
| **无告警通知** | 系统异常无法主动推送通知 | 🟡 中 |
| **日志文件无归档策略** | 只有简单的过期删除,无压缩归档 | 🟢 低 |
> 版本v2.0 | 适用于所有后端服务Python/Java/Go/Node.js
---
## 二、设计目标
## 一、核心原则
### 2.1 核心原则
1. **结构化** - 日志有固定 schema便于机器解析和查询
2. **可追** - 每个请求有唯一 ID可串联完整调用链路
3. **分级输出** - 不同环境输出不同级别,生产环境不输出 DEBUG
4. **安全合规** - 敏感数据自动脱敏token、密码、手机号等
5. **高性能** - 日志写入不影响业务接口性能(异步写入)
6. **可观测** - 支持快速检索、统计、告警
### 2.2 日志分级标准
| 级别 | 使用场景 | 示例 | 生产环境 |
|------|----------|------|:--------:|
| **DEBUG** | 开发调试 | 变量值、SQL 语句、完整堆栈 | ❌ 不输出 |
| **INFO** | 正常流程记录 | 任务创建成功、用户登录 | ✅ 记录 |
| **WARNING** | 可恢复异常 | 重试操作、参数校验失败、资源不足 | ✅ 记录 |
| **ERROR** | 需要人工介入 | 数据库连接失败、第三方 API 超时 | ✅ 记录 + 告警 |
| **CRITICAL** | 系统不可用 | 磁盘满、主节点宕机 | ✅ 记录 + 立即告警 |
| 原则 | 说明 |
|------|------|
| **结构化** | 所有日志必须输出为 JSON 格式,便于自动化采集和分析 |
| **可追** | 每个请求链路必须有唯一的 `traceId`,贯穿全流程 |
| **有上下文** | 每条日志必须包含足够的业务信息,能独立理解发生了什么 |
| **高性能** | 异步打印,禁止在业务主流程中同步写磁盘 |
| **安全合规** | 敏感信息自动脱敏禁止打印密码、token、身份证号等 |
| **可告警** | ERROR 日志必须触发实时告警,且有明确的错误分类 |
---
## 三、技术方案
## 二、日志分类
### 3.1 整体架构
生产环境必须按用途分流存储,**禁止所有日志混写在同一文件**
```
┌─────────────────────────────────────────────────────────────────────┐
│ 应用层 (Application Layer) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 数据集管理 │ │ 微调训练 │ │ 模型推理 │ │ 用户认证 │ ... │
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
│ │ │ │ │ │
│ └────────────┴───────────┴──────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Structured │ ← 结构化日志中间件 │
│ │ Logger │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────────┬────────────┬─────────────┐ │
│ ▼ ▼ ▼ │ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ Console │ │ File │ │ 审计DB │ │ 告警 │ │
│ │ (开发) │ │ (JSON) │ │ (PG) │ │(可选) │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 可观测层 (Observability) │
├─────────────────────────────────────────────────────────────┤
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Grafana │ │ Kibana │ │ PagerDuty │ ... │
│ │ (查询) │ │ (分析) │ │ (告警) │ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
```
| 分类 | 文件名示例 | 用途 | 保留周期 |
|------|-----------|------|----------|
| **业务日志** | `app-biz.log` | 记录核心业务流程(订单、支付、登录、任务状态变更等) | 7天热存 + 30天冷存 |
| **系统日志** | `app-sys.log` | 记录框架、中间件、连接池、GC、线程池状态 | 7天 |
| **访问日志** | `app-access.log` | 记录所有 HTTP/RPC 请求的入参、出参、耗时 | 15天用于审计 |
| **错误日志** | `app-error.log` | **仅记录 ERROR 级别**,含完整堆栈 | 30天用于复盘 |
### 3.2 日志 Schema 设计
**配置要点**
- 业务日志和错误日志必须独立文件,便于快速定位异常
- 框架类日志(如 `httpx``urllib3`)归入系统日志,且生产环境设为 WARN 级别
#### 3.2.1 应用日志 (app.log)
---
## 三、日志格式标准
### 3.1 统一 JSON 格式
所有日志必须输出为以下 JSON 结构,**字段名不得随意变更**
```json
{
"timestamp": "2026-08-17T10:30:00.000Z",
"@timestamp": "2026-08-19T10:30:45.123+08:00",
"level": "INFO",
"trace_id": "req-abc123",
"parent_span_id": "span-xyz789", // OpenTelemetry Span
"request": {
"method": "POST",
"path": "/dataset-manage",
"client_ip": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"user_id": "u_admin"
"logger": "com.order.service.OrderService",
"traceId": "abc-123-def-456",
"spanId": "span-001",
"userId": "U10086",
"message": "订单状态更新成功",
"fields": {
"orderId": "ORD-20260819-001",
"fromStatus": "PENDING",
"toStatus": "PAID",
"costMs": 23,
"retryCount": 0
},
"module": "dataset.router",
"function": "create_dataset",
"message": "数据集创建成功",
"extra": {
"dataset_id": "ds_abc123",
"dataset_name": "训练数据"
},
"duration_ms": 125,
"status_code": 200,
"error": null
"file": "OrderService.java:156",
"thread": "http-nio-8080-exec-8",
"host": "pod-order-7x9k2",
"app": "order-service",
"env": "prod"
}
```
#### 3.2.2 审计日志 (audit_logs 表)
### 3.2 字段说明
```sql
-- 已有表结构(保持不变)
CREATE TABLE IF NOT EXISTS audit_logs (
id TEXT PRIMARY KEY,
tenant_id TEXT,
project_id TEXT,
actor_id TEXT, -- 操作人
action TEXT, -- 操作类型: create/delete/update/acl.set/login...
target_type TEXT, -- 资源类型: dataset/model/fine-tune/user...
target_id TEXT, -- 资源 ID
detail TEXT, -- 详细信息 JSON
client_ip TEXT, -- 客户端 IP
time TEXT, -- 操作时间
-- 新增字段
trace_id TEXT, -- 关联应用日志的请求追踪 ID
request_method TEXT, -- HTTP 方法
request_path TEXT, -- 请求路径
status_code INTEGER, -- 响应状态码
duration_ms REAL, -- 耗时(ms)
extra JSONB -- 扩展信息
);
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `@timestamp` | string | ✅ | ISO8601 格式,带时区(如 `+08:00` |
| `level` | string | ✅ | DEBUG / INFO / WARNING / ERROR |
| `logger` | string | ✅ | 日志记录器名称,通常为类名 |
| `traceId` | string | ✅ | 全局唯一追踪ID从入口生成全链路透传 |
| `spanId` | string | 推荐 | 当前节点ID用于区分调用链中的不同服务 |
| `userId` | string | 业务必填 | 操作用户标识,未登录可为空 |
| `message` | string | ✅ | 人类可读的日志摘要,简洁明了 |
| `fields` | object | ✅ | 结构化业务字段,所有动态数据放入此处 |
| `file` | string | 推荐 | 代码文件名和行号 |
| `thread` | string | 推荐 | 线程名 |
| `host` | string | 推荐 | 主机名或 Pod 名称 |
| `app` | string | ✅ | 应用名称 |
| `env` | string | ✅ | dev / test / staging / prod |
| `error` | object | ERROR时必填 | 包含 `type``message``stack_trace` |
-- 新增索引
CREATE INDEX IF NOT EXISTS idx_audit_trace ON audit_logs(trace_id);
CREATE INDEX IF NOT EXISTS idx_audit_actor_time ON audit_logs(actor_id, time);
### 3.3 ERROR 日志额外字段
`level = ERROR` 时,必须包含:
```json
{
"error": {
"type": "ConnectionTimeoutError",
"message": "连接下游服务超时",
"stack_trace": "完整堆栈信息...",
"root_cause": "socket timeout after 3000ms"
}
}
```
### 3.3 日志中间件设计
---
## 四、日志内容规范
### 4.1 日志级别使用标准
| 级别 | 使用场景 | 示例 |
|------|----------|------|
| **DEBUG** | 开发调试信息,生产环境**默认关闭** | 变量值、中间计算结果 |
| **INFO** | 关键业务流程节点、状态变更、外部调用结果 | 订单创建成功、支付回调收到、任务状态变更 |
| **WARNING** | 可恢复的异常、降级处理、重试、资源使用超阈值 | 重试第3次成功、缓存穿透、磁盘使用率>80% |
| **ERROR** | 业务失败、系统异常、需要人工介入的错误 | 支付失败、数据库连接断开、第三方接口返回500 |
### 4.2 INFO 级别日志内容要求
每条 INFO 日志必须回答 **5W1H**
```
Who谁操作 + What做了什么 + When何时 + Where哪个服务/节点) + Why上下文 + How结果如何
```
**✅ 正例:**
```python
# backend/app/core/logging.py 新增
class StructuredLogger:
"""结构化日志记录器"""
def __init__(self, name: str):
self.logger = logging.getLogger(name)
self.trace_id = context_var.get("trace_id")
def info(self, msg: str, **kwargs):
self._log("INFO", msg, **kwargs)
def warning(self, msg: str, **kwargs):
self._log("WARNING", msg, **kwargs)
def error(self, msg: str, **kwargs):
self._log("ERROR", msg, **kwargs)
def _log(self, level: str, msg: str,
user_id: str = None,
target_type: str = None,
target_id: str = None,
duration_ms: float = None,
status_code: int = None,
error: Exception = None,
**extra):
"""统一日志记录方法"""
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"level": level,
"trace_id": self.trace_id.get(),
"request": {
"user_id": user_id or current_user_id(),
"client_ip": client_ip(),
# ...
},
"module": calling_module,
"message": msg,
"target": {
"type": target_type,
"id": target_id,
},
"extra": extra,
"duration_ms": duration_ms,
"error": format_exception(error) if error else None,
logger.info(
"任务日志拉取成功",
extra={
"userId": "U10086",
"fields": {
"jobId": "ft_a016cd8885cd",
"tailLines": 5000,
"logSize": "2.3MB",
"costMs": 42,
"source": "frontend"
}
# 1. 写入控制台/文件
self.logger.log(level, json.dumps(log_entry))
# 2. 异步写入审计表(如果需要)
if level in ("WARNING", "ERROR", "CRITICAL"):
async_write_audit(log_entry)
}
)
```
### 3.4 装饰器模式(推荐)
使用 Python 裁饰器自动记录,避免手动调用:
**❌ 反例(禁止):**
```python
# backend/app/core/log_decorator.py
def audit_log(action: str, target_type: str = ""):
"""审计日志装饰器"""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
result = await func(*args, **kwargs)
# 自动记录审计日志
record_audit(
action=action,
target_type=target_type,
target_id=kwargs.get('id') or result.get('id'),
detail=f"params={kwargs}"
)
return result
return wrapper
return decorator
# 使用示例
@audit_log("dataset.create", "dataset")
async def create_dataset(...):
# 业务逻辑
pass
logger.info("get logs success")
logger.info(f"job {job_id} status is {status}") # 禁止字符串拼接
```
### 3.5 敏感数据脱敏规则
### 4.3 WARNING/ERROR 日志内容要求
**必须包含三要素**
1. 发生了什么what
2. 为什么发生why—— 异常类型/错误码
3. 业务上下文context—— 哪个业务对象失败了
**✅ 正例:**
```python
logger.warning(
"计算轮询检测到任务失败",
extra={
"fields": {
"jobId": "ft_a016cd8885cd",
"failureReason": "GPU资源不足",
"errorCode": "RESOURCE_INSUFFICIENT",
"retryCount": 3,
"lastRetryTime": "2026-08-19T08:38:25.495+08:00"
}
}
)
```
**❌ 反例(禁止):**
```python
logger.warning("compute polling reported failures") # 没有任何上下文
logger.error(f"error: {e}") # 只打了异常信息没有业务ID
```
### 4.4 禁止打印的内容
| 类别 | 说明 |
|------|------|
| 密码/密钥 | 任何形式的 `password``secret``token``api_key` |
| 个人隐私 | 身份证号、手机号(需脱敏)、银行卡号 |
| 超大对象 | 超过 1KB 的 JSON/列表/文本内容 |
| 循环日志 | 禁止在 for/while 循环内打印 INFO 及以上级别 |
| 异常堆栈重复 | 同一异常在一个请求中只打印一次完整堆栈 |
---
## 五、链路追踪TraceId
### 5.1 基本原则
- **入口生成**:网关/前端/定时任务入口生成全局唯一的 `traceId`32位UUID
- **全链路透传**:通过 HTTP Header`X-Trace-Id`、RPC Meta、消息队列 Property 向下游传递
- **日志自动注入**:所有日志输出自动追加 `traceId`,代码中无需手动传入
- **跨线程传递**:使用 `MDC``ContextVars` 实现跨线程/协程的透传
### 5.2 实现要求
```python
# backend/app/core/masking.py
# Python 示例:使用 logging 的 Filter 自动注入 traceId
class TraceIdFilter(logging.Filter):
def filter(self, record):
record.traceId = get_current_trace_id() or "N/A"
return True
SENSITIVE_FIELDS = {
"token": "***",
"password": "***",
"phone": lambda x: f"{x[:3]}****{x[-4:]}",
"email": lambda x: x[0] + "***" + x.split("@")[1] if "@" in x else "***",
"id_card": lambda x: f"{x[:6]}********{x[-4:]}",
}
# 所有日志自动带上 traceId
logger.info("订单创建成功") # 自动注入 traceId代码无需传参
```
**❌ 绝对禁止**`traceId` 字段值为 `"-"``null`
---
## 六、性能与安全
### 6.1 性能要求
| 配置项 | 要求 |
|--------|------|
| **异步打印** | 必须使用异步 Appender禁止同步刷盘阻塞业务线程 |
| **单文件大小** | ≤ 1GB达到阈值自动滚动 |
| **滚动策略** | 按大小滚动(如 1GB或按天滚动 |
| **采样率** | 核心业务 100%,非核心(如健康检查、非关键查询)≤ 10% |
| **禁止打印循环** | 循环体内不得打印 INFO 及以上日志 |
| **大对象截断** | 超过 1KB 的内容自动截断前500字符 + 后500字符 |
### 6.2 安全要求
| 要求 | 说明 |
|------|------|
| **敏感字段自动脱敏** | 对 `mobile``idCard``password``token` 等字段自动掩码 |
| **脱敏规则** | 手机号:`138****5678`;身份证:`110***********1234` |
| **日志查询权限** | 生产日志平台必须有 RBAC 权限控制,禁止随意导出 |
| **审计追踪** | 谁在什么时候查询了哪些日志,必须记录审计日志 |
### 6.3 脱敏实现示例
```python
# 脱敏工具函数
def mask_sensitive(data: dict) -> dict:
"""递归脱敏字典中的敏感字段"""
for key, value in data.items():
if key in SENSITIVE_FIELDS:
data[key] = SENSITIVE_FIELDS[key](value) if callable(SENSITIVE_FIELDS[key]) else "***"
elif isinstance(value, dict):
mask_sensitive(value)
sensitive_keys = {"password", "token", "api_key", "mobile", "id_card"}
for key in sensitive_keys:
if key in data:
value = str(data[key])
if len(value) >= 11: # 手机号
data[key] = value[:3] + "****" + value[-4:]
elif len(value) >= 18: # 身份证
data[key] = value[:3] + "***********" + value[-4:]
return data
```
---
## 四、实施计划
## 七、运维与告警
### 4.1 Phase 1基础增强1-2 天)
### 7.1 日志采集架构
- [ ] **P1-1** 升级 `JsonLogFormatter`,增加 `trace_id` 字段
- [ ] **P1-2** 新增 `StructuredLogger` 封装类
- [ ] **P1-3** 统一所有模块的日志格式为 JSON
- [ ] **P1-4** 实现 `mask_sensitive()` 脱敏函数
- [ ] **P1-5** 审计日志表新增 `trace_id``duration_ms` 字段
```
应用日志(本地文件)
Filebeat轻量采集器
Kafka削峰填谷保证不丢
Logstash解析、过滤、脱敏
Elasticsearch索引存储
Kibana / Grafana查询展示
```
### 4.2 Phase 2自动化2-3 天)
**关键要求**
- 禁止应用直接写入 ES必须经过 Kafka 缓冲
- Filebeat 采集失败时必须有本地持久化和重试机制
- [ ] **P2-1** 编写 `@audit_log` 装饰器
- [ ] **P2-2** 为关键业务接口添加装饰器:
- 数据集 CRUD
- 模型 CRUD
- 微调任务创建/删除
- 用户登录/登出
- ACL 授权变更
- [ ] **P2-3** 实现日志异步写入队列(避免影响性能)
### 7.2 告警规则
### 4.3 Phase 3可观测性3-5 天)
| 条件 | 动作 | 优先级 |
|------|------|--------|
| 同一服务 5 分钟内出现 ≥ 3 次 ERROR | 钉钉/企微告警 + 电话P0级 | 最高 |
| 同一服务 10 分钟内 ERROR 率 > 5% | 钉钉告警P1级 | 高 |
| 磁盘使用率 > 80% | 钉钉告警P2级 | 中 |
| 单个 ERROR 堆栈重复出现 ≥ 10 次/分钟 | 聚合为一条告警,避免轰炸 | - |
- [ ] **P3-1** 集成 ELK Stack 或 Loki可选
- [ ] **P3-2** 编写 Grafana 仪表板:
- 请求量趋势图
- 错误率统计
- 慢接口 TOP10
- 用户操作审计面板
- [ ] [ ] **P3-3** 实现告警规则(错误率超阈值触发)
### 7.3 错误聚合策略
- 相同 `error.type` + 相同 `logger` + 相同堆栈前3行 → 视为同一类错误
- 同一类错误 5 分钟内只发 **1 条告警**(防告警轰炸)
- 告警内容必须包含:`app``env``error.type`、首次发生时间、最近发生时间、累计次数
---
## 五、配置示例
## 八、日志查询与使用规范
### 5.1 日志配置 (settings)
| 场景 | 查询方式 | 时效要求 |
|------|----------|----------|
| 日常运维 | Kibana 按 `traceId``userId` 检索 | 实时 |
| 异常排查 | 按 `app` + `level:ERROR` + 时间范围 | 实时 |
| 业务审计 | 按 `userId` + `logger:xxx` + 时间范围 | 30分钟内 |
| 性能分析 | 按 `costMs` 排序,找出慢请求 | 实时 |
| 安全审计 | 查询所有访问日志,按 IP/用户筛选 | 按需 |
```yaml
# config.yaml 或 .env
LOGGING:
level: INFO # 生产环境用 INFO开发用 DEBUG
dir: ./logs
file_prefix: app
max_bytes: 50MB # 单文件最大 50MB
retention_days: 30 # 保留 30 天
error_prefix: error # 错误日志单独文件
json: true # JSON 格式输出
---
AUDIT:
enabled: true
auto_record: true # 是否自动记录(通过装饰器)
sensitive_mask: true # 启用敏感数据脱敏
```
## 九、检查清单Code Review 必查)
### 5.2 日志输出示例
| 检查项 | 通过标准 |
|--------|----------|
| ☐ JSON 格式 | 所有日志输出均为 JSON字段名符合规范 |
| ☐ traceId | 所有日志都有 `traceId`,且不为 `"-"` |
| ☐ userId | 涉及用户操作的日志都有 `userId` |
| ☐ 业务上下文 | INFO 日志包含 `fields`有订单ID/任务ID等 |
| ☐ ERROR 日志 | 包含 `error.type` + `stack_trace` + 业务ID |
| ☐ 敏感信息 | 无密码/手机号明文,有脱敏处理 |
| ☐ 异步打印 | 使用异步 Appender |
| ☐ 日志级别 | 框架类日志 ≥ WARN业务日志分级合理 |
| ☐ 循环内日志 | 无循环内的 INFO 日志 |
| ☐ 日志分流 | 业务/系统/错误日志分文件存储 |
**控制台输出(开发环境):**
```
2026-08-17 18:30:00.123 | INFO | pid=12345 | MainThread | req=req-abc | dataset.router:create_dataset | dataset/router.py:45 | 数据集创建成功 {"dataset_id":"ds_abc"}
```
---
## 十、附:完整日志示例
### 示例一业务成功流程INFO
**文件输出JSON 格式):**
```json
{"@timestamp":"2026-08-17T18:30:00.123Z","level":"INFO","logger":"dataset.router","message":"数据集创建成功","module":"dataset.router","function":"create_dataset","file":"dataset/router.py","line":45,"process":12345,"thread":"MainThread","request_id":"req-abc","extra":{"dataset_id":"ds_abc"}}
{
"@timestamp": "2026-08-19T10:30:45.123+08:00",
"level": "INFO",
"logger": "app.services.job_service",
"traceId": "tracer-abc123xyz789",
"spanId": "span-001",
"userId": "U10086",
"message": "计算任务创建成功",
"fields": {
"jobId": "ft_a016cd8885cd",
"jobType": "fine_tuning",
"modelId": "model-llama2-7b",
"datasetId": "ds-20260819-001",
"gpuCount": 4,
"estimatedTime": "2h30m",
"costMs": 1523,
"source": "api"
},
"file": "job_service.py:234",
"thread": "MainThread",
"host": "compute-pod-7x9k2",
"app": "compute-service",
"env": "prod"
}
```
**审计日志查询 SQL**
```sql
-- 查询某用户最近7天的所有操作
SELECT time, action, target_type, target_id, detail, client_ip
FROM audit_logs
WHERE actor_id = 'u_admin'
AND time >= now() - interval '7 days'
ORDER BY time DESC;
### 示例二可恢复的警告WARNING
-- 查询某资源的授权变更历史
SELECT * FROM audit_logs
WHERE action LIKE '%acl%'
AND target_id = 'ds_abc123'
ORDER BY time DESC;
```json
{
"@timestamp": "2026-08-19T08:38:27.074+08:00",
"level": "WARNING",
"logger": "app.workers.compute_poller",
"traceId": "tracer-xyz789abc123",
"spanId": "span-002",
"userId": "U10086",
"message": "计算任务状态轮询检测到失败,进入重试",
"fields": {
"jobId": "ft_a016cd8885cd",
"currentStatus": "FAILED",
"failureReason": "GPU节点不可用",
"errorCode": "NODE_UNAVAILABLE",
"retryCount": 2,
"maxRetries": 3,
"nextRetryDelay": 30
},
"file": "compute_poller.py:45",
"thread": "Thread-8",
"host": "compute-pod-7x9k2",
"app": "compute-service",
"env": "prod"
}
```
### 示例三严重错误ERROR+ 告警
```json
{
"@timestamp": "2026-08-19T08:38:30.456+08:00",
"level": "ERROR",
"logger": "app.services.payment_service",
"traceId": "tracer-pay-789xyz",
"spanId": "span-003",
"userId": "U10086",
"message": "支付调用失败,订单状态回滚",
"fields": {
"orderId": "ORD-20260819-001",
"amount": 299.00,
"paymentMethod": "wechat",
"retryCount": 3,
"hasRollback": true
},
"error": {
"type": "PaymentTimeoutException",
"message": "支付网关超时等待响应超过5000ms",
"stack_trace": "Traceback (most recent call last):\n File \"payment_service.py:89\" ...",
"root_cause": "upstream gateway 10.0.1.100:8080 connection timeout"
},
"file": "payment_service.py:156",
"thread": "http-nio-8080-exec-12",
"host": "order-pod-3f8k1",
"app": "order-service",
"env": "prod"
}
```
### 示例四完整的请求访问日志ACCESS
```json
{
"@timestamp": "2026-08-19T10:30:45.001+08:00",
"level": "INFO",
"logger": "app.middleware.access_log",
"traceId": "tracer-abc123xyz789",
"userId": "U10086",
"message": "HTTP 请求完成",
"fields": {
"method": "POST",
"path": "/api/v1/jobs",
"statusCode": 200,
"clientIp": "192.168.1.100",
"userAgent": "Mozilla/5.0 ...",
"requestSize": 2048,
"responseSize": 512,
"costMs": 1523,
"requestBody": {"modelId": "model-llama2-7b", "datasetId": "ds-001"}, // 已脱敏
"responseBody": {"jobId": "ft_a016cd8885cd", "status": "CREATED"} // 已脱敏
},
"app": "compute-service",
"env": "prod"
}
```
---
## 、附录
## 十一、附录:技术栈配置速查
### A. 日志关键字段说明
### Python (Logging + JSON)
| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `trace_id` | string | 请求唯一标识,用于串联一次请求的所有日志 | `req-uuid-1234` |
| `parent_span_id` | string | 父 Span ID用于分布式追踪 | `span-parent-5678` |
| `actor_id` | string | 操作人用户 ID | `u_admin` |
| `action` | string | 操作动作 | `dataset.create`, `model.delete`, `login.success` |
| `target_type` | string | 操作的资源类型 | `dataset`, `trained_model`, `user` |
| `target_id` | string | 资源 ID | `ds_abc123` |
| `detail` | string/json | 操作详情 | `{"name": "训练数据", "type": "train"}` |
| `client_ip` | string | 客户端 IP | `192.168.1.100` |
| `duration_ms` | real | 接口耗时(ms) | `125.5` |
| `status_code` | int | HTTP 状态码 | `200`, `404`, `500` |
```python
import logging
import json
from pythonjsonlogger import jsonlogger
### B. 推荐的 Python 日志库对比
logger = logging.getLogger("app")
handler = logging.FileHandler("logs/app-biz.log")
formatter = jsonlogger.JsonFormatter(
fmt="%(asctime)s %(levelname)s %(name)s %(traceId)s %(message)s",
rename_fields={"asctime": "@timestamp", "name": "logger"}
)
handler.setFormatter(formatter)
logger.addHandler(handler)
```
| 库 | 特点 | 适用场景 |
|-----|------|---------|
| `structlog` | 结构化日志,高性能 | 推荐 ✅ |
| `loguru` | 简单易用,自动配置 | 小型项目 |
| `logging` | Python 标准库 | 当前已使用 |
### Java (Logback + JSON)
### C. 参考链接
```xml
<!-- logback-spring.xml -->
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<fieldNames>
<timestamp>@timestamp</timestamp>
<level>level</level>
<thread>thread</thread>
<logger>logger</logger>
</fieldNames>
</encoder>
</appender>
```
- [Python logging cookbook](https://docs.python.org/3/howto/logging.html)
- [ELK Stack 官方文档](https://www.elastic.co/guide/index.html)
- [OpenTelemetry 规范](https://opentelemetry.io/docs/)
### Go (Zap + JSON)
```go
logger, _ := zap.NewProduction()
logger.Info("订单创建成功",
zap.String("traceId", traceId),
zap.String("userId", userId),
zap.String("orderId", orderId),
zap.Int64("costMs", costMs),
)
```