Files
cs-note/hzh/MS/04-可观测性/02-日志系统.md
T
2026-05-24 11:42:38 +08:00

150 lines
5.1 KiB
Markdown

---
tags: [microservice, logging, elk, loki, structured-logging]
create time: 2026-05-05
---
# 日志系统
## 概述
Logging 回答的问题是:**具体发生了什么?**——通过原始事件记录提供上下文。
在微服务架构中,所有服务的日志必须集中收集。散落在各台机器上的日志无法支撑有效的故障排查。
```mermaid
flowchart LR
APP[应用容器] -->|stdout/stderr| COLLECTOR[采集器<br/>FLUENTD / Filebeat]
COLLECTOR --> PARSE["解析 & 过滤"]
PARSE --> ES[(Elasticsearch)]
PARSE --> LOKI[(Loki)]
ES --> GRAF["Grafana / Kibana"]
LOKI --> GRAF
style COLLECTOR fill:#fff3e0
style PARSE fill:#e3f2fd
```
## 结构化日志
**不要写纯文本日志**。推荐 JSON 格式,便于程序解析和查询:
```json
{
"timestamp": "2026-05-05T10:30:00Z",
"level": "ERROR",
"service": "order-service",
"trace_id": "abc-123-def",
"span_id": "span-456",
"msg": "failed to connect payment service",
"caller": "payment/client.go:42",
"cost_ms": 30000,
"user_id": "user-42"
}
```
### 最小字段集合(必选)
| 字段 | 格式 | 说明 |
|------|------|------|
| `timestamp` | ISO 8601 (`2026-05-05T10:30:00Z`) | UTC 时区,不可省略 |
| `level` | TRACE / DEBUG / INFO / WARN / ERROR / FATAL | 大小写统一 |
| `service` | 字符串 | 微服务名称 |
| `trace_id` | 字符串 | 链路追踪 ID,与 Tracing 串联 |
| `msg` | 字符串 | 人类可读的消息描述 |
### 日志级别使用规范
| 级别 | 使用场景 | 示例 |
|------|---------|------|
| **TRACE** | 调试级详细输出(仅开发环境) | 方法入参/出参、循环体内部状态 |
| **DEBUG** | 诊断信息 | 缓存命中率、连接池状态 |
| **INFO** | 关键业务事件 | 下单成功、支付回调、用户注册 |
| **WARN** | 异常但不致命 | 重试了一次、走降级、配置热更新 |
| **ERROR** | 影响单个请求的失败 | DB 超时、下游调用失败、空指针 |
| **FATAL** | 进程无法继续运行 | 配置文件缺失、依赖服务完全不可用 |
> [!warning] 最佳实践
>
> - **不要记录敏感信息**:密码、Token、身份证号等绝不能出现在日志中
> - **INFO 级记录关键业务事件**——这些是排查业务问题的主线
> - **WARN 级记录异常但不致命**——如重试、降级
> - **ERROR 级必须有 trace_id 和错误堆栈**,否则毫无价值
## 日志采集管线
```mermaid
flowchart LR
App["业务应用"] -->|stdout/stderr| K8sPod["Pod 容器日志"]
K8sPod --> Collector["采集器<br/>Filebeat / FluentBit"]
Collector --> Backend["存储后端<br/>Elasticsearch / Loki"]
Backend --> UI["查询面板<br/>Grafana / Kibana"]
Collector -->|"过滤掉 health check"| Drop["丢弃无用日志"]
style Drop fill:#ffebee,stroke:#ef5350
```
### 采集器选型
| 组件 | 语言 | 资源消耗 | 特点 |
|------|------|---------|------|
| **Filebeat** (ELK) | Go | 低 | 轻量,适合 K8s DaemonSet |
| **Fluentd** | Ruby | 中 | 插件生态丰富,处理灵活 |
| **FluentBit** | C | 极低 | 边缘场景首选,K8s 官方推荐 |
## 日志方案对比
| 方案 | 存储 | 查询能力 | 成本 | 适用场景 |
|------|------|---------|------|---------|
| **ELK** (Elasticsearch) | ES 集群 | 全文检索强大 | 高 | 日志量大、需要复杂分析 |
| **Loki + Grafana** | 对象存储 (S3) | Label 索引 + LogQL | 低 | 已在用 Grafana 的团队 |
| **云 SLS / CloudWatch** | 云托管 | 开箱即用 | 中~高 | 全云原生环境 |
### Loki vs ELK 决策树
```mermaid
flowchart TD
Q{"需要全文检索吗?"}
Q -- "否,按时间/Service/TraceId 查询" --> LOKI["✅ Loki<br/>低成本、轻量"]
Q -- "是,复杂的全文搜索" --> ELK["✅ ELK<br/>功能强大"]
LOKI --> Check{"日日志量 > 1TB?"}
Check -- 是 --> ConsiderES["考虑混合架构<br/>高频用 Loki,低频归档到 ES"]
Check -- 否 --> Done["直接用 Loki ✅"]
ELK --> Check2{"团队有经验?"}
Check2 -- 是 --> UseES["直接上 ES ✅"]
Check2 -- 否 --> Managed["用托管版 (阿里云 SLS)"]
```
## 采样策略
生产环境中并非所有日志都要采集。**正常路径采 1%,异常路径全量**。
```
正常 HTTP 200 → 1% 采样率 → 每小时 ~360 条日志
异常 HTTP 5xx → 100% 全量采集 → 可能数千条日志
DB 慢查询 → 100% 全量采集 → 重点关注
健康检查 → 不采集 → 零成本
```
> [!tip] 实现方式
> 采样逻辑建议写在日志库的中间件层,而非业务代码里:
> ```go
> func SampleLogger(next logger.Logger, rate float64) logger.Logger {
> return &sampledLogger{
> next: next,
> shouldSample: func() bool {
> return rand.Float64() < rate
> },
> }
> }
> ```
## 关联笔记
- [[04-可观测性/01-Metrics监控]] — Metrics + Logging = 完整的问题定位能力
- [[04-可观测性/03-链路追踪]] — trace_id 是日志与追踪的桥梁
- [[04-可观测性/04-告警管理]] — 日志可以触发基于模式的告警