日志系统设计
日志的重要性
日志是系统可观测性的三大支柱之一(日志、指标、链路追踪)。良好的日志系统可以帮助我们:
- 故障排查:快速定位问题根因
- 行为审计:记录用户操作和系统变更
- 监控告警:基于日志内容触发告警
- 数据分析:从日志中挖掘业务洞察
日志级别
const (
DEBUG = 0 // 调试信息,开发环境使用
INFO = 1 // 常规信息,系统正常运行
WARN = 2 // 警告,可能存在问题
ERROR = 3 // 错误,需要关注
FATAL = 4 // 致命错误,系统不可用
)
级别使用规范
| 级别 | 使用场景 | 示例 |
|---|---|---|
| DEBUG | 开发调试 | SQL 语句、函数入参 |
| INFO | 业务记录 | 用户注册、订单创建 |
| WARN | 异常但不影响 | 重试操作、配置缺失 |
| ERROR | 功能不可用 | DB 连接失败、请求超时 |
| FATAL | 进程无法继续 | 端口被占用、配置错误 |
结构化日志
结构化日志使用键值对格式(如 JSON),便于机器解析和搜索:
// 不推荐:非结构化
log.Printf("User %s logged in from IP %s", userID, ip)
// 推荐:结构化
logger.Info("user_login",
log.String("user_id", userID),
log.String("ip", ip),
log.String("user_agent", ua),
log.Duration("duration", dur),
)
输出示例:
{
"level": "info",
"time": "2024-01-15T10:30:00Z",
"message": "user_login",
"user_id": "u12345",
"ip": "192.168.1.1",
"duration_ms": 42
}
日志收集架构
传统 ELK 方案
应用 → Filebeat → Logstash → Elasticsearch → Kibana
轻量级 Loki 方案
应用 → Promtail → Loki → Grafana
推荐架构
应用 → 标准输出 → 容器 stdout → 日志采集 Agent → 消息队列 → 存储 → 查询
采集 Agent:Filebeat / Fluentd / Promtail
消息队列:Kafka(可选,用于流量缓冲)
存储:Elasticsearch / Loki / ClickHouse
查询:Kibana / Grafana
日志实践规范
必须包含的字段
{
"timestamp": "2024-01-15T10:30:00.123Z",
"level": "ERROR",
"service": "user-service",
"trace_id": "abc123def456",
"message": "database connection failed",
"error": {
"type": "connection_refused",
"message": "dial tcp 127.0.0.1:5432: connect: connection refused"
},
"metadata": {
"host": "pod-123",
"environment": "production",
"version": "1.2.3"
}
}
注意事项
DO:
- 包含 trace_id 支持链路关联
- 记录请求耗时用于性能分析
- 日志及时刷新(设置 buffer 或即时刷新)
- 敏感信息脱敏(密码、Token、身份证号)
// 脱敏示例
func maskEmail(email string) string {
parts := strings.Split(email, "@")
if len(parts) != 2 {
return email
}
return parts[0][:1] + "***@" + parts[1]
}
DON'T:
- 不要在循环中输出日志
- 不要记录用户的密码和密钥
- 不要使用 string 拼接构造日志消息
- 不要将日志级别设置为 DEBUG 上生产
日志轮转
// Go 中使用 lumberjack 实现日志轮转
logger := &lumberjack.Logger{
Filename: "/var/log/app/app.log",
MaxSize: 100, // MB
MaxAge: 30, // 天
MaxBackups: 10,
Compress: true,
}