577 lines
16 KiB
Markdown
577 lines
16 KiB
Markdown
# Light-Sentry 日志分析系统设计文档
|
||
|
||
## 1. 项目概述
|
||
|
||
### 1.1 背景
|
||
基于用户需求,需要构建一个日志分析系统,支持接收 Sentry 前后端 SDK 的上报,并在 **2核2G ECS + 阿里云OSS** 的资源限制下稳定运行。
|
||
|
||
### 1.2 核心目标
|
||
- **兼容性**:完全兼容 Sentry SDK 的上报协议(DSN、事件格式)
|
||
- **轻量级**:适配 2核2G 服务器资源
|
||
- **低成本**:利用阿里云 OSS 进行冷存储,降低成本
|
||
- **可视化**:提供友好的日志查询和可视化界面
|
||
|
||
---
|
||
|
||
## 2. 系统架构
|
||
|
||
### 2.1 整体架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 客户端 SDK 层 │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Browser SDK │ │ Node SDK │ │ Python SDK │ │
|
||
│ │ @sentry/browser │ @sentry/node │ @sentry/python │ │
|
||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||
│ │ │ │ │
|
||
│ └─────────────────┼─────────────────┘ │
|
||
│ ▼ │
|
||
│ ┌───────────────────────┐ │
|
||
│ │ Sentry API 网关 │ │
|
||
│ │ /api/{project}/store │ │
|
||
│ └───────────┬───────────┘ │
|
||
└───────────────────────────┼─────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 服务端处理层 │
|
||
│ ┌──────────────────┐ ┌──────────────────┐ │
|
||
│ │ 事件处理器 │──▶│ 事件转换器 │ │
|
||
│ │ (Event Handler)│ │ (Event Converter)│ │
|
||
│ └──────────────────┘ └──────────┬───────┘ │
|
||
│ │ │
|
||
│ ┌───────────────────────┼───────────────────────┐ │
|
||
│ ▼ ▼ ▼ │
|
||
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
|
||
│ │ Loki 索引 │ │ Loki 存储 │ │ OSS 持久化 │ │
|
||
│ │ (索引元数据) │ │ (近期日志) │ │ (历史归档) │ │
|
||
│ └───────────────┘ └───────────────┘ └───────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 可视化查询层 │
|
||
│ ┌───────────────┐ │
|
||
│ │ Grafana │ │
|
||
│ │ (查询/图表) │ │
|
||
│ └───────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 核心组件说明
|
||
|
||
| 组件 | 技术选型 | 角色 | 资源占用 |
|
||
|------|---------|------|---------|
|
||
| **API网关** | Express/FastAPI | 接收Sentry协议上报 | ~100MB |
|
||
| **日志存储** | Loki | 索引和近期日志存储 | ~500MB |
|
||
| **持久化层** | 阿里云OSS | 历史日志归档 | 按需扩展 |
|
||
| **可视化** | Grafana | 日志查询和图表展示 | ~300MB |
|
||
|
||
**总内存预估**:~1GB(保留1GB给系统和缓冲)
|
||
|
||
---
|
||
|
||
## 3. 技术方案
|
||
|
||
### 3.1 Sentry 协议兼容层
|
||
|
||
#### 3.1.1 协议说明
|
||
|
||
Sentry SDK 使用 DSN (Data Source Name) 配置上报地址:
|
||
```
|
||
{PROTOCOL}://{PUBLIC_KEY}@{HOST}/{PATH}/{PROJECT_ID}
|
||
```
|
||
|
||
SDK 向 `/api/{PROJECT_ID}/store/` 端点发送 POST 请求,数据格式为 JSON。
|
||
|
||
#### 3.1.2 事件数据结构(简化版)
|
||
|
||
```json
|
||
{
|
||
"event_id": "abc123...",
|
||
"timestamp": "2024-01-01T00:00:00Z",
|
||
"level": "error",
|
||
"logger": "javascript",
|
||
"platform": "javascript",
|
||
"message": "Uncaught TypeError: Cannot read property",
|
||
"exception": {
|
||
"values": [{
|
||
"type": "TypeError",
|
||
"value": "Cannot read property 'x' of undefined",
|
||
"stacktrace": {
|
||
"frames": [{
|
||
"filename": "app.js",
|
||
"lineno": 42,
|
||
"function": "doSomething"
|
||
}]
|
||
}
|
||
}]
|
||
},
|
||
"tags": {
|
||
"environment": "production",
|
||
"release": "v1.0.0"
|
||
},
|
||
"contexts": {
|
||
"request": {
|
||
"url": "https://example.com/page",
|
||
"method": "GET"
|
||
},
|
||
"user": {
|
||
"id": "12345",
|
||
"email": "user@example.com"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3.1.3 API 网关设计
|
||
|
||
**技术选型**:Node.js + Express(轻量级,内存占用低)
|
||
|
||
**核心端点**:
|
||
|
||
| 端点 | 方法 | 功能 |
|
||
|------|------|------|
|
||
| `/api/:projectId/store/` | POST | 接收 Sentry SDK 上报事件 |
|
||
| `/health` | GET | 健康检查 |
|
||
|
||
**设计要点**:
|
||
- 支持 Sentry 的 DSN 认证(通过 `X-Sentry-Auth` 头或查询参数)
|
||
- 异步处理,不阻塞请求响应
|
||
- 事件格式校验和标准化
|
||
- 支持批量上报
|
||
|
||
### 3.2 日志存储方案
|
||
|
||
#### 3.2.1 Loki 配置
|
||
|
||
**为什么选择 Loki**:
|
||
- 轻量级,内存占用远低于 ELK(Elasticsearch 需要至少 4GB)
|
||
- 基于标签索引,查询效率高
|
||
- 与 Grafana 无缝集成
|
||
- 支持 OSS 作为后端存储
|
||
|
||
**关键配置**:
|
||
|
||
```yaml
|
||
auth_enabled: false
|
||
|
||
server:
|
||
http_listen_port: 3100
|
||
|
||
ingester:
|
||
lifecycler:
|
||
address: 127.0.0.1
|
||
ring:
|
||
kvstore:
|
||
store: inmemory
|
||
replication_factor: 1
|
||
final_sleep: 0s
|
||
chunk_idle_period: 1h
|
||
chunk_retain_period: 30s
|
||
max_transfer_retries: 0
|
||
|
||
schema_config:
|
||
configs:
|
||
- from: 2024-01-01
|
||
store: boltdb-shipper
|
||
object_store: aws
|
||
schema: v11
|
||
index:
|
||
prefix: loki_index_
|
||
period: 24h
|
||
|
||
storage_config:
|
||
aws:
|
||
s3: s3://{AK}:{SK}@{REGION}/{BUCKET}
|
||
s3forcepathstyle: true
|
||
boltdb_shipper:
|
||
active_index_directory: /data/loki/index
|
||
cache_location: /data/loki/cache
|
||
shared_store: s3
|
||
|
||
limits_config:
|
||
reject_old_samples: true
|
||
reject_old_samples_max_age: 168h
|
||
|
||
chunk_store_config:
|
||
max_look_back_period: 0s
|
||
|
||
table_manager:
|
||
retention_deletes_enabled: true
|
||
retention_period: 30d
|
||
```
|
||
|
||
#### 3.2.2 OSS 存储策略
|
||
|
||
**存储层级**:
|
||
|
||
| 层级 | 用途 | 存储类型 | 成本 |
|
||
|------|------|---------|------|
|
||
| **标准存储** | 近期日志(30天内) | OSS标准 | 0.12元/GB/月 |
|
||
| **低频存储** | 中期日志(30-90天) | OSS低频 | 0.07元/GB/月 |
|
||
| **归档存储** | 历史日志(90天以上) | OSS归档 | 0.014元/GB/月 |
|
||
|
||
**生命周期管理**:
|
||
```xml
|
||
<LifecycleConfiguration>
|
||
<Rule>
|
||
<ID>transition-to-infrequent-access</ID>
|
||
<Filter>
|
||
<Prefix>loki/</Prefix>
|
||
</Filter>
|
||
<Status>Enabled</Status>
|
||
<Transition>
|
||
<Days>30</Days>
|
||
<StorageClass>IA</StorageClass>
|
||
</Transition>
|
||
<Transition>
|
||
<Days>90</Days>
|
||
<StorageClass>Archive</StorageClass>
|
||
</Transition>
|
||
<Expiration>
|
||
<Days>365</Days>
|
||
</Expiration>
|
||
</Rule>
|
||
</LifecycleConfiguration>
|
||
```
|
||
|
||
### 3.3 事件处理流程
|
||
|
||
```
|
||
SDK上报 → API网关 → 格式校验 → 标签提取 → Loki写入 → OSS持久化
|
||
↓
|
||
索引元数据存储
|
||
```
|
||
|
||
**标签提取策略**(用于 Loki 查询):
|
||
|
||
| 标签名 | 来源字段 | 用途 |
|
||
|--------|---------|------|
|
||
| `project` | `project_id` | 项目隔离 |
|
||
| `environment` | `tags.environment` | 环境区分 |
|
||
| `level` | `level` | 日志级别过滤 |
|
||
| `platform` | `platform` | 平台分类 |
|
||
| `release` | `tags.release` | 版本追踪 |
|
||
| `logger` | `logger` | 日志来源 |
|
||
|
||
### 3.4 可视化方案
|
||
|
||
#### 3.4.1 Grafana 配置
|
||
|
||
**数据源配置**:
|
||
- 类型:Loki
|
||
- URL:`http://localhost:3100`
|
||
- 认证:无
|
||
|
||
**查询示例**:
|
||
|
||
```logql
|
||
// 查询所有 error 级别日志
|
||
{level="error"} |= "error"
|
||
|
||
// 查询特定项目的日志
|
||
{project="my-project", environment="production"}
|
||
|
||
// 查询包含特定错误信息的日志
|
||
{level="error"} |= "TypeError"
|
||
|
||
// 统计错误数量
|
||
count_over_time({level="error"}[1m])
|
||
```
|
||
|
||
**仪表盘设计**:
|
||
|
||
| 面板 | 功能 | 查询 |
|
||
|------|------|------|
|
||
| 错误趋势 | 实时错误数量折线图 | `count_over_time({level="error"}[1m])` |
|
||
| 环境分布 | 各环境错误占比饼图 | `sum(count_over_time({level="error"}[1h])) by (environment)` |
|
||
| 平台分布 | 各平台错误占比饼图 | `sum(count_over_time({level="error"}[1h])) by (platform)` |
|
||
| 最新错误 | 最近错误列表 | `{level="error"} | tail 10` |
|
||
| Top错误类型 | 错误类型排名 | `topk(5, count by (exception_type) (count_over_time({level="error"}[1h])))` |
|
||
|
||
---
|
||
|
||
## 4. 部署方案
|
||
|
||
### 4.1 服务器配置
|
||
|
||
**ECS 规格**:2核2G(阿里云 ecs.g6.large 或同等规格)
|
||
|
||
**操作系统**:Ubuntu 22.04 LTS
|
||
|
||
**磁盘配置**:
|
||
- 系统盘:40GB SSD
|
||
- 数据盘:20GB SSD(用于 Loki 索引缓存)
|
||
|
||
### 4.2 资源分配
|
||
|
||
| 组件 | CPU | 内存 |
|
||
|------|-----|------|
|
||
| 系统预留 | 0.5核 | 512MB |
|
||
| API网关 (Node.js) | 0.5核 | 256MB |
|
||
| Loki | 1核 | 768MB |
|
||
| Grafana | 0.5核 | 512MB |
|
||
|
||
### 4.3 Docker Compose 部署
|
||
|
||
```yaml
|
||
version: '3.8'
|
||
|
||
services:
|
||
light-sentry-api:
|
||
image: light-sentry/api:latest
|
||
ports:
|
||
- "9000:9000"
|
||
environment:
|
||
- LOKI_URL=http://loki:3100
|
||
- OSS_ENDPOINT=${OSS_ENDPOINT}
|
||
- OSS_ACCESS_KEY=${OSS_ACCESS_KEY}
|
||
- OSS_SECRET_KEY=${OSS_SECRET_KEY}
|
||
- OSS_BUCKET=${OSS_BUCKET}
|
||
depends_on:
|
||
- loki
|
||
restart: unless-stopped
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: '0.5'
|
||
memory: 256M
|
||
|
||
loki:
|
||
image: grafana/loki:2.9.0
|
||
ports:
|
||
- "3100:3100"
|
||
volumes:
|
||
- ./loki/config:/etc/loki
|
||
- ./loki/data:/data/loki
|
||
environment:
|
||
- AWS_ACCESS_KEY_ID=${OSS_ACCESS_KEY}
|
||
- AWS_SECRET_ACCESS_KEY=${OSS_SECRET_KEY}
|
||
restart: unless-stopped
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: '1'
|
||
memory: 768M
|
||
|
||
grafana:
|
||
image: grafana/grafana:10.2.0
|
||
ports:
|
||
- "3000:3000"
|
||
volumes:
|
||
- ./grafana/data:/var/lib/grafana
|
||
- ./grafana/provisioning:/etc/grafana/provisioning
|
||
environment:
|
||
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
|
||
depends_on:
|
||
- loki
|
||
restart: unless-stopped
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: '0.5'
|
||
memory: 512M
|
||
```
|
||
|
||
### 4.4 Nginx 反向代理
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name logs.example.com;
|
||
|
||
location /api/ {
|
||
proxy_pass http://localhost:9000/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
client_max_body_size 10m;
|
||
}
|
||
|
||
location /grafana/ {
|
||
proxy_pass http://localhost:3000/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. SDK 接入指南
|
||
|
||
### 5.1 前端 SDK 配置
|
||
|
||
```javascript
|
||
import * as Sentry from '@sentry/browser';
|
||
|
||
Sentry.init({
|
||
dsn: 'https://public_key@logs.example.com/api/1/store/',
|
||
environment: 'production',
|
||
release: 'v1.0.0',
|
||
tracesSampleRate: 1.0,
|
||
});
|
||
```
|
||
|
||
### 5.2 后端 SDK 配置(Node.js)
|
||
|
||
```javascript
|
||
import * as Sentry from '@sentry/node';
|
||
|
||
Sentry.init({
|
||
dsn: 'https://public_key@logs.example.com/api/2/store/',
|
||
environment: 'production',
|
||
release: 'v1.0.0',
|
||
tracesSampleRate: 1.0,
|
||
});
|
||
```
|
||
|
||
### 5.3 后端 SDK 配置(Python)
|
||
|
||
```python
|
||
import sentry_sdk
|
||
|
||
sentry_sdk.init(
|
||
dsn="https://public_key@logs.example.com/api/3/store/",
|
||
environment="production",
|
||
release="v1.0.0",
|
||
traces_sample_rate=1.0,
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 性能优化策略
|
||
|
||
### 6.1 内存优化
|
||
|
||
| 策略 | 说明 |
|
||
|------|------|
|
||
| Loki 使用 OSS 作为后端 | 减少本地磁盘和内存占用 |
|
||
| API网关使用流处理 | 避免一次性加载大请求 |
|
||
| 限制并发连接数 | 使用 Nginx 限流 |
|
||
|
||
### 6.2 存储优化
|
||
|
||
| 策略 | 说明 |
|
||
|------|------|
|
||
| OSS 生命周期管理 | 自动降级存储类型 |
|
||
| Loki 压缩 | 启用 Snappy 压缩 |
|
||
| 日志采样 | 高频日志采样处理 |
|
||
|
||
### 6.3 查询优化
|
||
|
||
| 策略 | 说明 |
|
||
|------|------|
|
||
| 标签索引 | 使用标签过滤而非全文搜索 |
|
||
| 查询缓存 | Grafana 缓存查询结果 |
|
||
| 时间范围限制 | 默认查询最近24小时 |
|
||
|
||
---
|
||
|
||
## 7. 监控与告警
|
||
|
||
### 7.1 健康检查
|
||
|
||
| 检查项 | 端点 | 频率 |
|
||
|--------|------|------|
|
||
| API网关 | `/health` | 30秒 |
|
||
| Loki | `/ready` | 30秒 |
|
||
| Grafana | `/api/health` | 30秒 |
|
||
|
||
### 7.2 告警规则
|
||
|
||
| 告警项 | 条件 | 通知方式 |
|
||
|--------|------|---------|
|
||
| API错误率 | 错误率 > 5% | 钉钉/邮件 |
|
||
| 服务不可用 | 健康检查失败 | 钉钉/邮件 |
|
||
| 内存使用率 | 内存 > 85% | 钉钉/邮件 |
|
||
| OSS存储告警 | 存储 > 80% | 阿里云告警 |
|
||
|
||
---
|
||
|
||
## 8. 安全策略
|
||
|
||
### 8.1 认证授权
|
||
|
||
- API 网关支持 Sentry DSN 认证
|
||
- Grafana 启用基础认证
|
||
- Nginx 配置访问控制列表
|
||
|
||
### 8.2 数据加密
|
||
|
||
- 传输层:HTTPS/TLS 1.2+
|
||
- 存储层:OSS 服务端加密(SSE-KMS)
|
||
|
||
### 8.3 访问日志
|
||
|
||
- Nginx 访问日志记录
|
||
- API 网关请求日志
|
||
|
||
---
|
||
|
||
## 9. 成本预估
|
||
|
||
### 9.1 服务器成本
|
||
|
||
| 资源 | 规格 | 月费用 |
|
||
|------|------|--------|
|
||
| ECS | 2核2G | ~80元 |
|
||
| 数据盘 | 20GB SSD | ~10元 |
|
||
| **合计** | - | **~90元/月** |
|
||
|
||
### 9.2 OSS 存储成本
|
||
|
||
假设日日志量:1GB
|
||
|
||
| 存储类型 | 容量 | 月费用 |
|
||
|----------|------|--------|
|
||
| 标准存储 | 30GB | ~3.6元 |
|
||
| 低频存储 | 60GB | ~4.2元 |
|
||
| 归档存储 | 275GB | ~3.85元 |
|
||
| **合计** | - | **~11.65元/月** |
|
||
|
||
### 9.3 总成本
|
||
|
||
**~100元/月**(含服务器和存储)
|
||
|
||
---
|
||
|
||
## 10. 扩展规划
|
||
|
||
### 10.1 短期(3个月内)
|
||
|
||
- 完成核心功能开发
|
||
- 集成 Sentry 协议
|
||
- 部署上线
|
||
|
||
### 10.2 中期(6个月内)
|
||
|
||
- 增加日志采样功能
|
||
- 优化查询性能
|
||
- 增加告警功能
|
||
|
||
### 10.3 长期(1年内)
|
||
|
||
- 支持更多日志来源
|
||
- 增加分布式追踪
|
||
- 考虑升级到更强配置
|
||
|
||
---
|
||
|
||
## 附录:组件版本建议
|
||
|
||
| 组件 | 推荐版本 | 备注 |
|
||
|------|---------|------|
|
||
| Node.js | 20.x LTS | API网关运行环境 |
|
||
| Express | 4.x | API框架 |
|
||
| Loki | 2.9.x | 日志存储 |
|
||
| Grafana | 10.x | 可视化 |
|
||
| Docker | 24.x | 容器化部署 |
|
||
|
||
---
|
||
|
||
**文档版本**: v1.0
|
||
**创建日期**: 2026-06-16
|
||
**适用场景**: 2核2G ECS + 阿里云OSS 资源受限环境 |