请求日志与监控
使用 OpenTelemetry 和 OpenObserve 追踪所有 HTTP 请求,用于审计、分析和性能监控
请求日志与监控
ProductReady 内置了基于 OpenTelemetry 和 OpenObserve 的中心化请求日志系统。追踪每个 HTTP 请求(API 调用、页面访问、tRPC、OpenAPI),用于审计跟踪、分析和性能监控。
注重隐私:仅记录元数据(方法、路径、状态、耗时)— 不记录请求体或敏感数据。
快速开始
启动 OpenObserve
# 启动所有服务(包括 OpenObserve)
make db
# 或: docker-compose up -d
# 验证运行状态
curl http://localhost:5080/healthz
# {"status":"ok"}启用请求日志
在 .env 文件中:
# 启用请求日志(默认禁用)
ENABLE_REQUEST_LOGGING="true"
# OpenObserve 端点
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:5080/api/default"
# serviceName 自动从 appConfig.name 获取,version 从 package.json 读取
# OpenObserve 认证
OPENOBSERVE_EMAIL="admin@productready.dev"
OPENOBSERVE_PASSWORD="productready"重启应用
pnpm dev
# 你应该看到:
# [OTelLogger] ✅ Initialized (serviceName=productready)在 OpenObserve 中查看日志
- 打开:http://localhost:5080
- 登录:
admin@productready.dev/productready - 导航到:Logs → Search
- 选择 stream:
productready_request_logs
记录什么数据?
每个请求记录:
- 用户 ID(已认证)或 "anonymous"
- 会话 ID(来自 Better Auth)
- IP 地址(从 Cloudflare/Vercel 头提取)
- User Agent(浏览器/设备信息)
- HTTP 方法(GET, POST, PUT, DELETE 等)
- URL 路径(
/api/users/123) - 状态码(200, 404, 500 等)
- 耗时(响应时间,毫秒)
- 时间戳(ISO 8601 格式)
自动排除的路径
- 静态资源:
/_next/,*.ico,*.png,*.css,*.js - 健康检查:
/health,/ping - 认证回调:
/api/auth/callback/*
OpenObserve 字段名映射
OpenObserve v0.14.0 将点分隔的 JSON 键名展平为下划线。查询时请使用展平后的名称。
| JSON 键名(日志发送) | OpenObserve 列名(查询使用) |
|---|---|
http.request.method | http_request_method |
http.response.status_code | http_response_status_code |
url.path | url_path |
http.duration_ms | http_duration_ms |
user.id | user_id |
user.session_id | user_session_id |
client.address | client_address |
user_agent.original | user_agent_original |
service.name | service_name |
在 OpenObserve 中查询日志
基础查询
http_response_status_code > 0显示所有已记录的请求及其状态码。
http_response_status_code >= 500筛选服务器错误(5xx 状态码)。
http_duration_ms > 1000查找耗时超过 1 秒的请求。
user_id = 'user_abc123'追踪特定用户的所有请求。
高级查询
特定端点的请求:
url_path LIKE '/api/users/%'匿名 vs 已认证:
user_id = 'anonymous' -- 匿名用户
user_id != 'anonymous' -- 已认证用户按 IP 地址筛选:
client_address = '203.0.113.42'按时间筛选:
@timestamp > now() - 1h -- 最近 1 小时
@timestamp > now() - 24h -- 最近 24 小时架构
┌─────────────┐
│ 请求 │
└──────┬──────┘
│
▼
┌─────────────────────┐
│ Next.js 中间件 │ (proxy.ts → withRequestLogging)
└──────┬──────────────┘
│
┌───┴───┐
│ │
▼ ▼
Traces Logs
(OTel) (HTTP POST)
│ │
▼ ▼
┌─────────────────────┐
│ OpenObserve │ http://localhost:5080
│ (OLAP 数据库) │ Traces + Logs + 仪表板
└─────────────────────┘双管道架构:
- Traces(OTel SDK → OTLP):分布式追踪,性能 spans
- Logs(直接 HTTP →
/_json):结构化请求审计日志,可搜索
主要优势:
- ✅ 非阻塞:日志被缓冲并异步发送(<1ms 影响)
- ✅ 可扩展:支持 10 万+ 请求/天
- ✅ 灵活:可切换到 Grafana Tempo、Jaeger 或任何 OTLP 端点
- ✅ 成本效益:OLAP 存储比 PostgreSQL 便宜
配置
环境变量
| 变量 | 描述 | 默认值 | 必需 |
|---|---|---|---|
ENABLE_REQUEST_LOGGING | 启用/禁用日志 | false | 否 |
OTEL_EXPORTER_OTLP_ENDPOINT | OpenObserve 端点 | http://localhost:5080/api/default | 是 |
OPENOBSERVE_EMAIL | OpenObserve 登录邮箱 | admin@productready.dev | 是 |
OPENOBSERVE_PASSWORD | OpenObserve 密码 | productready | 是 |
OPENOBSERVE_ORG_ID | 组织 ID | default | 否 |
注意:
serviceName自动从appConfig.name获取,serviceVersion从package.json读取 — 无需额外环境变量。
性能影响
基准测试
| 指标 | 数值 |
|---|---|
| 日志开销 | <1ms/请求(缓冲) |
| 最大吞吐量 | 10万+ 请求/天 |
| 内存使用 | ~50MB(批量缓冲) |
| 日志批量刷新 | 20 条或 5 秒间隔 |
| Trace 批量刷新 | 512 spans 或 5 秒间隔 |
故障排查
检查 1:日志是否启用?
echo $ENABLE_REQUEST_LOGGING
# 应输出:true检查 2:OpenObserve 是否运行?
curl http://localhost:5080/healthz
# 应返回:{"status":"ok"}检查 3:控制台日志
pnpm dev
# 应看到:[OTelLogger] Initialized with endpoint: ...检查 4:发送测试请求
curl http://localhost:3000/
# 然后检查 OpenObserve UI在 otel-logger.ts 中减小批量大小:
maxQueueSize: 512, // 从 2048 减少
maxExportBatchSize: 128, // 从 512 减少为高流量应用启用采样:
// 仅记录 10% 的请求
if (Math.random() > 0.1) return;错误:ECONNREFUSED
OpenObserve 未运行。使用以下命令启动:
make db # 或: docker-compose up -d错误:401 Unauthorized
检查 .env 中的 OPENOBSERVE_EMAIL 和 OPENOBSERVE_PASSWORD错误:'Search field not found'
OpenObserve 会将点分隔的键名展平为下划线。
查询时使用 user_id 而非 "user.id"。
在 OpenObserve UI → stream schema 中查看实际字段名。隐私与合规
GDPR 合规
请求日志是可选的(默认禁用)。为符合 GDPR:
- 告知用户:在隐私政策中说明
- 保留策略:30-90 天后删除日志
- 删除权利:提供端点删除用户日志
- 匿名化 IP:记录前哈希 IP 地址(可选)
数据最小化
仅记录必要的元数据:
- ❌ 不记录请求体
- ❌ 不记录响应体
- ❌ 不记录 cookies(会话 ID 除外)
- ❌ 不记录密码或 API 密钥
- ✅ 仅记录:方法、路径、状态、耗时、用户 ID