2. 日志基础理论:日志级别、格式标准与结构化日志
日志是系统可观测性的基石。在 ErrorHandler 报警联动体系中,日志的质量直接决定了故障定位的速度与准确度。本章从日志级别、格式标准、结构化与非结构化日志三个核心维度展开,为后续的日志采集、解析与报警联动打下理论基础。
2.1 日志级别(Log Level)
日志级别定义了日志事件的严重程度。标准 Syslog 协议(RFC 5424)定义了 8 个级别,但在工程实践中,通常使用以下 5 个级别:
| 级别 | 数值 | 含义 | 典型场景 | 报警联动策略 |
|---|---|---|---|---|
| DEBUG | 7 | 调试信息,仅在开发/测试环境启用 | 变量值、函数调用栈、SQL 语句 | 不触发报警,仅用于本地排查 |
| INFO | 6 | 正常业务流程的关键节点 | 服务启动、请求到达、任务完成 | 不触发报警,用于监控大盘统计 |
| WARN | 4 | 潜在风险或非预期行为,但系统仍可运行 | 重试次数超阈值、配置降级、慢查询 | 触发低优先级告警(P4/P5),通知值班群 |
| ERROR | 3 | 功能不可用或数据损坏,需要立即关注 | 数据库连接失败、API 返回 5xx、消息丢失 | 触发高优先级告警(P1/P2),自动创建工单 |
| FATAL | 2 | 系统崩溃或数据永久丢失,无法自动恢复 | OOM、磁盘损坏、主从同步中断 | 触发最高优先级告警(P0),自动拉起应急预案 |
工程建议:生产环境建议将日志级别设置为 INFO 或 WARN,避免 DEBUG 日志造成磁盘 IO 和存储成本飙升。ERROR 和 FATAL 日志应作为报警联动的核心触发源。
2.2 日志格式标准
统一的日志格式是日志采集、解析和报警联动的先决条件。业界主流标准包括:
2.2.1 Syslog 格式(RFC 5424)
Syslog 是 Unix/Linux 系统日志的标准协议,格式如下:
<PRI>VERSION TIMESTAMP HOSTNAME APPNAME PROCID MSGID STRUCTURED-DATA MSG
示例:
<14>1 2025-06-15T10:30:00.123Z api-gateway-01 nginx 1234 - [meta sequenceId="98765"] GET /users 200 12ms
2.2.2 通用日志格式(Common Log Format, CLF)
Web 服务器(如 Nginx、Apache)广泛使用的格式:
127.0.0.1 - frank [15/Jun/2025:10:30:00 +0800] "GET /api/v1/users HTTP/1.1" 200 2326
2.2.3 企业级推荐格式(JSON 结构化)
现代微服务架构中,推荐使用 JSON 格式,便于日志中心(如 ELK、Loki)直接解析:
{
"timestamp": "2025-06-15T10:30:00.123Z",
"level": "ERROR",
"logger": "com.example.order.OrderService",
"thread": "http-nio-8080-exec-3",
"message": "订单支付超时",
"context": {
"orderId": "ORD-20250615-001",
"userId": "u_12345",
"amount": 299.99,
"retryCount": 3
},
"exception": {
"type": "PaymentTimeoutException",
"stackTrace": "at com.example.order.OrderService.pay(OrderService.java:85)"
}
}
格式规范要点:
- 时间戳:统一使用 ISO 8601 格式(带时区),避免跨时区解析歧义。
- 日志级别:必须大写,作为报警联动的第一筛选条件。
- 上下文信息:包含请求 ID、用户 ID、业务 ID,便于链路追踪。
- 异常堆栈:ERROR/FATAL 级别必须附带完整堆栈,用于自动聚类。
2.3 结构化日志 vs 非结构化日志
这是日志工程中最核心的设计决策之一。两者的核心差异如下:
| 对比维度 | 结构化日志 | 非结构化日志 |
|---|---|---|
| 格式 | JSON、Avro、Protobuf 等预定义 Schema | 纯文本、自由格式字符串 |
| 可解析性 | 机器可直接解析,无需正则匹配 | 依赖正则表达式或 Grok 模式提取字段 |
| 查询效率 | 支持字段级索引,查询速度快 | 全文检索,性能较低 |
| 报警联动 | 可直接提取 errorCode、duration 等字段触发条件 | 需先解析文本,增加延迟和出错概率 |
| 存储成本 | 略高(字段名重复),但可压缩 | 较低,但解析成本高 |
| 变更兼容 | Schema 演进需版本管理 | 格式变更后解析规则需同步更新 |
| 典型场景 | 微服务、云原生、分布式系统 | 遗留系统、简单脚本、临时调试 |
2.4 工程实践:日志格式模板
以下是一个 Python 项目中推荐的结构化日志配置示例(使用 structlog 库):
import structlog
import logging
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.JSONRenderer()
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
logger = structlog.get_logger("order_service")
# 使用示例
logger.error("订单支付超时", order_id="ORD-20250615-001", amount=299.99, retry_count=3)
常见陷阱:
- 日志级别滥用:将业务异常全部打为 ERROR,导致报警风暴。应区分业务异常(WARN)和系统异常(ERROR)。
- 敏感信息泄露:结构化日志中避免记录密码、、身份证号等敏感字段,或使用脱敏处理器。
- 日志丢失:异步日志在高并发下可能丢失,ERROR/FATAL 级别建议使用同步写入。