非结构化日志处理
介绍
非结构化日志是指没有固定格式的日志数据,通常包含自由文本、多行消息或混合格式的内容(如错误堆栈、应用程序输出等)。这类日志在调试和分析时极具价值,但由于缺乏统一的模式,处理起来比结构化日志更复杂。Grafana Loki通过日志管道和Promtail等工具,提供了高效处理非结构化日志的能力。
关键概念
- 日志行(Log Line): 原始的非结构化文本(如
"ERROR: Failed to connect to database"
)。 - 标签(Labels): 用于索引日志的键值对(如
job="api-server"
)。 - 提取(Extraction): 从日志中解析出结构化字段(如将时间戳提取为
timestamp
)。
非结构化日志的挑战
- 格式多样性:不同服务可能输出完全不同的日志格式。
- 多行日志:例如Java异常堆栈可能跨越多行。
- 高基数问题:避免使用动态值(如请求ID)作为标签。
处理步骤
1. 日志收集(以Promtail为例)
使用Promtail收集非结构化日志并添加基础标签:
yaml
# promtail-config.yaml
scrape_configs:
- job_name: app_logs
static_configs:
- targets: [localhost]
labels:
job: 'my_app'
__path__: '/var/log/app/*.log'
2. 日志提取
通过Pipeline Stages从非结构化日志中提取字段。例如,使用 regex
提取错误级别:
yaml
pipeline_stages:
- regex:
expression: '^(?P<level>\\w+): (?P<message>.+)'
- labels:
level:
输入日志示例:
ERROR: Disk full
输出结果:
- 提取字段:
level="ERROR"
,message="Disk full"
- 新增标签:
level="ERROR"
3. 处理多行日志
使用 multiline
阶段合并堆栈跟踪:
yaml
- multiline:
firstline: '^\$$\\d{4}-\\d{2}-\\d{2}'
max_wait_time: 3s
匹配以日期开头的行作为多日志组的起始行。
实际案例:解析Nginx访问日志
原始日志(非结构化):
192.168.1.1 - - [10/Jul/2023:14:30:22 +0000] "GET /api/users HTTP/1.1" 200 1234
Promtail配置:
yaml
- regex:
expression: '^(?P<ip>\\S+) \\S+ \\S+ \$$(?P<timestamp>[^\$$]+)\$$ "(?P<method>\\S+) (?P<path>\\S+) HTTP/\\d\\.\\d" (?P<status>\\d+) (?P<size>\\d+)'
- labels:
method:
status:
提取后的结构化字段:
json
{
"ip": "192.168.1.1",
"timestamp": "10/Jul/2023:14:30:22 +0000",
"method": "GET",
"path": "/api/users",
"status": "200",
"size": "1234"
}
高级技巧
使用LogQL进一步处理
在Grafana中通过LogQL过滤非结构化日志:
logql
{job="my_app"} |= "error"
| json | status >= 500
性能优化
注意
避免过度使用标签!每个唯一标签组合会创建新的数据流。
总结
非结构化日志处理的核心步骤:
- 收集:通过Promtail配置日志源和基础标签。
- 提取:使用Pipeline Stages解析关键字段。
- 存储:合理选择标签避免高基数问题。
延伸练习
- 尝试从自定义应用程序日志中提取
request_id
字段。 - 配置Promtail将多行Java异常合并为单个日志条目。