更新内容: - 移除 `dashboard.json` 文件,清理不再需要的 Grafana 仪表板配置。 - 简化项目目录结构,删除多余的监控配置以优化维护。
565 lines
12 KiB
Markdown
565 lines
12 KiB
Markdown
# Loki 日志收集系统集成文档
|
||
|
||
## 概述
|
||
|
||
本项目已集成 Grafana Loki 日志收集系统,支持两种日志收集模式:
|
||
|
||
1. **Docker stdio 收集**(推荐)- 从容器标准输出/错误收集日志
|
||
2. **Log 文件收集**(备用)- 从日志文件收集日志
|
||
|
||
## 架构
|
||
|
||
```
|
||
应用容器 (stdout/stderr)
|
||
↓
|
||
Docker Engine
|
||
↓
|
||
Promtail (日志采集器)
|
||
↓
|
||
Loki (日志存储)
|
||
↓
|
||
Grafana (可视化)
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
### 1. 启动服务
|
||
|
||
```bash
|
||
cd deployment
|
||
docker-compose up -d
|
||
```
|
||
|
||
这将启动以下服务:
|
||
- **app**: 应用服务 (端口 8111)
|
||
- **loki**: 日志存储服务 (端口 3100)
|
||
- **promtail**: 日志采集服务 (端口 9080)
|
||
- **grafana**: 可视化服务 (端口 3000)
|
||
- **prometheus**: 指标收集服务 (端口 9090)
|
||
- **redis**: 缓存服务 (端口 6380)
|
||
|
||
### 2. 访问 Grafana
|
||
|
||
1. 打开浏览器访问 http://localhost:3000
|
||
2. 使用默认凭据登录:
|
||
- 用户名: `admin`
|
||
- 密码: `admin`
|
||
3. 首次登录后建议修改密码
|
||
|
||
### 3. 查看日志
|
||
|
||
#### 方式 1: 使用预配置的日志仪表板
|
||
|
||
1. 在 Grafana 左侧菜单点击 **Dashboards**
|
||
2. 选择 **日志监控** 仪表板
|
||
3. 查看以下面板:
|
||
- **日志流 (实时)**: 实时日志流
|
||
- **日志量趋势(按级别)**: 时间序列图表
|
||
- **日志级别分布**: 按级别统计
|
||
- **错误日志**: 只显示 ERROR 级别日志
|
||
|
||
#### 方式 2: 使用 Explore 功能
|
||
|
||
1. 在 Grafana 左侧菜单点击 **Explore** (指南针图标)
|
||
2. 选择 **Loki** 数据源
|
||
3. 输入 LogQL 查询语句(见下文)
|
||
|
||
## LogQL 查询示例
|
||
|
||
### 基础查询
|
||
|
||
```logql
|
||
# 查询所有应用日志
|
||
{job="functional-scaffold-app"}
|
||
|
||
# 查询特定级别的日志
|
||
{job="functional-scaffold-app", level="ERROR"}
|
||
{job="functional-scaffold-app", level="INFO"}
|
||
|
||
# 查询特定容器的日志
|
||
{container="functional-scaffold-app-1"}
|
||
```
|
||
|
||
### 文本过滤
|
||
|
||
```logql
|
||
# 包含特定文本
|
||
{job="functional-scaffold-app"} |= "request_id"
|
||
|
||
# 不包含特定文本
|
||
{job="functional-scaffold-app"} != "healthz"
|
||
|
||
# 正则表达式匹配
|
||
{job="functional-scaffold-app"} |~ "error|exception"
|
||
|
||
# 正则表达式不匹配
|
||
{job="functional-scaffold-app"} !~ "debug|trace"
|
||
```
|
||
|
||
### JSON 字段提取
|
||
|
||
```logql
|
||
# 提取 request_id 字段
|
||
{job="functional-scaffold-app"} | json | request_id != ""
|
||
|
||
# 提取并过滤特定 request_id
|
||
{job="functional-scaffold-app"} | json | request_id = "abc123"
|
||
|
||
# 提取 logger 字段
|
||
{job="functional-scaffold-app"} | json | logger = "functional_scaffold.api.routes"
|
||
```
|
||
|
||
### 聚合查询
|
||
|
||
```logql
|
||
# 统计日志数量
|
||
count_over_time({job="functional-scaffold-app"}[5m])
|
||
|
||
# 按级别统计
|
||
sum by (level) (count_over_time({job="functional-scaffold-app"}[5m]))
|
||
|
||
# 计算错误率
|
||
sum(rate({job="functional-scaffold-app", level="ERROR"}[5m]))
|
||
/
|
||
sum(rate({job="functional-scaffold-app"}[5m]))
|
||
```
|
||
|
||
## 日志收集模式
|
||
|
||
### 模式 1: Docker stdio 收集(默认,推荐)
|
||
|
||
**特点:**
|
||
- 无需修改应用代码
|
||
- 自动收集容器标准输出/错误
|
||
- 性能影响极小
|
||
- 配置简单
|
||
|
||
**工作原理:**
|
||
1. 应用将日志输出到 stdout/stderr
|
||
2. Docker Engine 捕获日志
|
||
3. Promtail 通过 Docker API 读取日志
|
||
4. 日志发送到 Loki 存储
|
||
|
||
**配置:**
|
||
- 应用容器需要添加标签:
|
||
```yaml
|
||
labels:
|
||
logging: "promtail"
|
||
logging_jobname: "functional-scaffold-app"
|
||
```
|
||
|
||
### 模式 2: Log 文件收集(备用)
|
||
|
||
**特点:**
|
||
- 日志持久化到文件
|
||
- 支持日志轮转
|
||
- 适合需要本地日志文件的场景
|
||
|
||
**启用方式:**
|
||
|
||
1. 修改 `deployment/docker-compose.yml`:
|
||
```yaml
|
||
environment:
|
||
- LOG_FILE_ENABLED=true
|
||
- LOG_FILE_PATH=/var/log/app/app.log
|
||
```
|
||
|
||
2. 重启服务:
|
||
```bash
|
||
docker-compose up -d app
|
||
```
|
||
|
||
**日志文件配置:**
|
||
- 最大文件大小: 100MB
|
||
- 保留备份数: 5 个
|
||
- 总存储空间: 最多 500MB
|
||
|
||
## 配置说明
|
||
|
||
### Loki 配置 (monitoring/loki.yaml)
|
||
|
||
```yaml
|
||
limits_config:
|
||
retention_period: 168h # 日志保留 7 天
|
||
ingestion_rate_mb: 10 # 摄入速率限制 10MB/s
|
||
ingestion_burst_size_mb: 20 # 突发大小 20MB
|
||
```
|
||
|
||
**可调整参数:**
|
||
- `retention_period`: 日志保留时间(默认 7 天)
|
||
- `ingestion_rate_mb`: 每秒摄入速率限制
|
||
- `ingestion_burst_size_mb`: 突发流量大小
|
||
|
||
### Promtail 配置 (monitoring/promtail.yaml)
|
||
|
||
**Docker stdio 收集配置:**
|
||
```yaml
|
||
scrape_configs:
|
||
- job_name: docker
|
||
docker_sd_configs:
|
||
- host: unix:///var/run/docker.sock
|
||
filters:
|
||
- name: label
|
||
values: ["logging=promtail"]
|
||
```
|
||
|
||
**文件收集配置:**
|
||
```yaml
|
||
scrape_configs:
|
||
- job_name: app_files
|
||
static_configs:
|
||
- targets:
|
||
- localhost
|
||
labels:
|
||
job: functional-scaffold-app-files
|
||
__path__: /var/log/app/*.log
|
||
```
|
||
|
||
## 验证和测试
|
||
|
||
### 1. 检查服务状态
|
||
|
||
```bash
|
||
# 查看所有服务
|
||
docker-compose ps
|
||
|
||
# 检查 Loki 健康状态
|
||
curl http://localhost:3100/ready
|
||
|
||
# 检查 Promtail 健康状态
|
||
curl http://localhost:9080/ready
|
||
```
|
||
|
||
### 2. 生成测试日志
|
||
|
||
```bash
|
||
# 发送测试请求
|
||
curl -X POST http://localhost:8111/invoke \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"algorithm": "PrimeChecker", "params": {"number": 17}}'
|
||
```
|
||
|
||
### 3. 查询日志
|
||
|
||
```bash
|
||
# 使用 Loki API 查询
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"}' \
|
||
--data-urlencode 'limit=10' \
|
||
| jq '.data.result'
|
||
```
|
||
|
||
### 4. 在 Grafana 中验证
|
||
|
||
1. 访问 http://localhost:3000/explore
|
||
2. 选择 Loki 数据源
|
||
3. 输入查询: `{job="functional-scaffold-app"}`
|
||
4. 应该能看到应用日志
|
||
|
||
## 故障排查
|
||
|
||
### 问题 1: 看不到日志
|
||
|
||
**检查步骤:**
|
||
|
||
1. 确认 Promtail 正在运行:
|
||
```bash
|
||
docker-compose ps promtail
|
||
```
|
||
|
||
2. 检查 Promtail 日志:
|
||
```bash
|
||
docker-compose logs promtail
|
||
```
|
||
|
||
3. 确认应用容器有正确的标签:
|
||
```bash
|
||
docker inspect functional-scaffold-app-1 | grep -A 5 Labels
|
||
```
|
||
|
||
4. 检查 Loki 是否接收到日志:
|
||
```bash
|
||
curl -G -s "http://localhost:3100/loki/api/v1/label/job/values" | jq
|
||
```
|
||
|
||
### 问题 2: Promtail 无法访问 Docker socket
|
||
|
||
**错误信息:**
|
||
```
|
||
permission denied while trying to connect to the Docker daemon socket
|
||
```
|
||
|
||
**解决方案:**
|
||
|
||
在 macOS/Linux 上,确保 Docker socket 权限正确:
|
||
```bash
|
||
sudo chmod 666 /var/run/docker.sock
|
||
```
|
||
|
||
或者将 Promtail 容器添加到 docker 组(Linux):
|
||
```yaml
|
||
promtail:
|
||
user: root
|
||
group_add:
|
||
- docker
|
||
```
|
||
|
||
### 问题 3: 日志量过大
|
||
|
||
**症状:**
|
||
- Loki 响应缓慢
|
||
- 磁盘空间不足
|
||
|
||
**解决方案:**
|
||
|
||
1. 调整日志保留期:
|
||
```yaml
|
||
# monitoring/loki.yaml
|
||
limits_config:
|
||
retention_period: 72h # 改为 3 天
|
||
```
|
||
|
||
2. 增加摄入速率限制:
|
||
```yaml
|
||
limits_config:
|
||
ingestion_rate_mb: 5 # 降低到 5MB/s
|
||
```
|
||
|
||
3. 添加日志过滤:
|
||
```yaml
|
||
# monitoring/promtail.yaml
|
||
pipeline_stages:
|
||
- match:
|
||
selector: '{job="functional-scaffold-app"}'
|
||
stages:
|
||
- drop:
|
||
expression: ".*healthz.*" # 丢弃健康检查日志
|
||
```
|
||
|
||
### 问题 4: 文件模式下看不到日志
|
||
|
||
**检查步骤:**
|
||
|
||
1. 确认文件日志已启用:
|
||
```bash
|
||
docker-compose exec app env | grep LOG_FILE
|
||
```
|
||
|
||
2. 检查日志文件是否存在:
|
||
```bash
|
||
docker-compose exec app ls -lh /var/log/app/
|
||
```
|
||
|
||
3. 检查 Promtail 是否能访问日志文件:
|
||
```bash
|
||
docker-compose exec promtail ls -lh /var/log/app/
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
### 1. 减少日志量
|
||
|
||
**在应用层面:**
|
||
- 调整日志级别为 WARNING 或 ERROR
|
||
- 过滤掉不必要的日志(如健康检查)
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
environment:
|
||
- LOG_LEVEL=WARNING
|
||
```
|
||
|
||
**在 Promtail 层面:**
|
||
```yaml
|
||
# monitoring/promtail.yaml
|
||
pipeline_stages:
|
||
- drop:
|
||
expression: ".*healthz.*"
|
||
drop_counter_reason: "healthcheck"
|
||
```
|
||
|
||
### 2. 优化查询性能
|
||
|
||
**使用标签过滤:**
|
||
```logql
|
||
# 好:使用标签过滤(快)
|
||
{job="functional-scaffold-app", level="ERROR"}
|
||
|
||
# 差:使用文本过滤(慢)
|
||
{job="functional-scaffold-app"} |= "ERROR"
|
||
```
|
||
|
||
**限制时间范围:**
|
||
```logql
|
||
# 查询最近 5 分钟
|
||
{job="functional-scaffold-app"}[5m]
|
||
|
||
# 避免查询过长时间范围
|
||
{job="functional-scaffold-app"}[7d] # 慢
|
||
```
|
||
|
||
### 3. 存储优化
|
||
|
||
**定期清理旧数据:**
|
||
```bash
|
||
# Loki 会自动根据 retention_period 清理
|
||
# 也可以手动清理
|
||
docker-compose exec loki rm -rf /loki/chunks/*
|
||
```
|
||
|
||
**监控磁盘使用:**
|
||
```bash
|
||
docker-compose exec loki du -sh /loki/chunks
|
||
```
|
||
|
||
## 高级功能
|
||
|
||
### 1. 告警规则
|
||
|
||
在 Loki 中配置告警规则(需要 Loki Ruler):
|
||
|
||
```yaml
|
||
# monitoring/loki-rules.yaml
|
||
groups:
|
||
- name: error_alerts
|
||
interval: 1m
|
||
rules:
|
||
- alert: HighErrorRate
|
||
expr: |
|
||
sum(rate({job="functional-scaffold-app", level="ERROR"}[5m]))
|
||
/
|
||
sum(rate({job="functional-scaffold-app"}[5m]))
|
||
> 0.05
|
||
for: 5m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "错误率过高"
|
||
description: "应用错误率超过 5%"
|
||
```
|
||
|
||
### 2. 日志导出
|
||
|
||
**导出为 JSON:**
|
||
```bash
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"}' \
|
||
--data-urlencode 'start=2024-01-01T00:00:00Z' \
|
||
--data-urlencode 'end=2024-01-02T00:00:00Z' \
|
||
| jq '.data.result' > logs.json
|
||
```
|
||
|
||
**导出为文本:**
|
||
```bash
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"}' \
|
||
| jq -r '.data.result[].values[][1]' > logs.txt
|
||
```
|
||
|
||
### 3. 与 Prometheus 集成
|
||
|
||
在 Grafana 仪表板中同时显示日志和指标:
|
||
|
||
```json
|
||
{
|
||
"panels": [
|
||
{
|
||
"title": "错误率和错误日志",
|
||
"targets": [
|
||
{
|
||
"datasource": "Prometheus",
|
||
"expr": "rate(http_requests_total{status=\"error\"}[5m])"
|
||
},
|
||
{
|
||
"datasource": "Loki",
|
||
"expr": "{job=\"functional-scaffold-app\", level=\"ERROR\"}"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 最佳实践
|
||
|
||
### 1. 日志格式
|
||
|
||
**使用结构化日志(JSON):**
|
||
```python
|
||
logger.info("处理请求", extra={
|
||
"request_id": "abc123",
|
||
"user_id": "user456",
|
||
"duration": 0.123
|
||
})
|
||
```
|
||
|
||
**输出:**
|
||
```json
|
||
{
|
||
"asctime": "2024-01-01 12:00:00,000",
|
||
"name": "functional_scaffold.api.routes",
|
||
"levelname": "INFO",
|
||
"message": "处理请求",
|
||
"request_id": "abc123",
|
||
"user_id": "user456",
|
||
"duration": 0.123
|
||
}
|
||
```
|
||
|
||
### 2. 标签策略
|
||
|
||
**好的标签:**
|
||
- 低基数(值的种类少)
|
||
- 用于过滤和分组
|
||
- 例如:`level`, `logger`, `container`
|
||
|
||
**不好的标签:**
|
||
- 高基数(值的种类多)
|
||
- 例如:`request_id`, `user_id`, `timestamp`
|
||
|
||
**正确做法:**
|
||
```logql
|
||
# 使用标签过滤
|
||
{job="functional-scaffold-app", level="ERROR"}
|
||
|
||
# 使用 JSON 提取高基数字段
|
||
{job="functional-scaffold-app"} | json | request_id = "abc123"
|
||
```
|
||
|
||
### 3. 查询优化
|
||
|
||
**使用时间范围:**
|
||
```logql
|
||
{job="functional-scaffold-app"}[5m] # 最近 5 分钟
|
||
```
|
||
|
||
**限制返回行数:**
|
||
```logql
|
||
{job="functional-scaffold-app"} | limit 100
|
||
```
|
||
|
||
**使用聚合减少数据量:**
|
||
```logql
|
||
sum by (level) (count_over_time({job="functional-scaffold-app"}[5m]))
|
||
```
|
||
|
||
## 参考资料
|
||
|
||
- [Loki 官方文档](https://grafana.com/docs/loki/latest/)
|
||
- [LogQL 查询语言](https://grafana.com/docs/loki/latest/logql/)
|
||
- [Promtail 配置](https://grafana.com/docs/loki/latest/clients/promtail/configuration/)
|
||
- [Grafana Explore](https://grafana.com/docs/grafana/latest/explore/)
|
||
|
||
## 总结
|
||
|
||
本项目的 Loki 集成提供了:
|
||
|
||
✅ **开箱即用** - 无需额外配置即可收集日志
|
||
✅ **双模式支持** - Docker stdio(默认)和文件收集
|
||
✅ **自动化配置** - 数据源和仪表板自动加载
|
||
✅ **结构化日志** - JSON 格式,支持字段提取
|
||
✅ **高性能** - 低资源占用,快速查询
|
||
✅ **易于扩展** - 支持自定义标签和过滤规则
|
||
|
||
如有问题,请参考故障排查章节或查阅官方文档。
|