# 什么是loki
Loki是Grafana开源的一个轻量级日志系统。相对于传统的ELK日志系统,Loki占用的系统资源更少、速度更快,非常适合不需要复杂功能的中小型系统
官方文档 https://grafana.com/docs/loki/latest/ 官方中文文档 https://grafana.org.cn/docs/loki/latest/
# 主要框架结构
参考官方文档(https://grafana.org.cn/docs/loki/latest/get-started/),Loki系统主要需要以下几个部分:
- Grafana:前端系统,用于显示查询界面
- Loki:主服务器,用于收集每个终端上传后的日志,对日志进行排序与聚合,并提供查询服务给grafana
- Promtail:日志上传代理,用于监控每个节点的日志并将日志发送到loki服务器。需要部署在每个产生日志的节点或Pod中,收集并将日志发送到Loki系统中
- 输出日志的服务:业务中需要被监控日志的服务,(建议将服务的日志输出路径规范化)
- Grafana系统在各种监控中经常使用。因此在这个框架系统中,可以复用其他查询系统中已搭建的Grafana系统
- Loki系统的搭建比较简单,对于小型系统,可以直接使用默认配置文件,基于Docker或二进制文件启动。这里不讨论
- 本文主要讨论针对Promtail程序匹配各种格式的输出日志遇到的问题进行讨论
# 了解 Promtail
- Promtail 是一个代理,它将本地日志的内容发送到私有的 Grafana Loki 实例或 Grafana Cloud。它通常部署在运行需要监控的应用程序的每台机器或Pod上。
- 在 Promtail 可以将任何数据从日志文件发送到 Loki 之前,对日志信息进行处理。例如,对日志进行正则表达式匹配,时间戳转换,格式转换等
一个最简单的Promtail配置如下:
|
|
其中:
- positions: promtail的位置配置文件路径
- clients:需要推送到的loki服务端api的url
- scrape_configs:日志抓取配置
- jobname:定义抓取任务
- static_configs:静态配置,指定要抓取的日志文件路径和标签信息。
- 这种方式直接将本地的log文件收集,并传送到loki系统中。对于简单使用,这样已经足够
- 但问题来了:常见的日志查询系统如ElasticSearch,Loki等,都希望传入的日志能被格式化为格式化的字段(如Json),从而更好的进行搜索。
- 我们希望promtail在处理日志时,也能进行对应的转换
# Promtail中的pipeline
因此对于进一步使用,实际上的需求为:
- 读取按行输出的日志时,将其转为json格式
- 需要处理Java、Python等异常抛出时的多行堆栈信息,即多行为一个信息的情况
- 对部分信息(如时间戳等)进行格式转换
针对这些需求,Promtail中提供了pipeline阶段进行各种处理
官方文档:
- https://grafana.org.cn/docs/loki/latest/send-data/promtail/pipelines/
- https://grafana.org.cn/docs/loki/latest/send-data/promtail/stages/
在 Promtail 中一个 pipeline 管道被用来转换一个单一的日志行、标签和它的时间戳。简单来说,一个 pipeline 管道是由一组 stages 阶段组成的,在 Promtail 配置中一共有 4 种类型的 stages。:
- Parsing stages(解析阶段) 用于解析当前的日志行并从中提取数据,提取的数据可供其他阶段使用。
- Transform stages(转换阶段) 用于对之前阶段提取的数据进行转换。
- Action stages(处理阶段) 用于从以前阶段中提取数据并对其进行处理,包括:
- 添加或修改现有日志行标签
- 更改日志行的时间戳
- 修改日志行内容
- 在提取的数据基础上创建一个 metrics 指标
- Filtering stages(过滤阶段) 可选择应用一个阶段的子集,或根据一些条件删除日志数据。
一个典型的 pipeline 将从解析阶段开始(如 regex 或 json 阶段)从日志行中提取数据。然后有一系列的处理阶段配置,对提取的数据进行处理。最后将数据输出到Loki
因此,针对我们的需求(处理多行异常日志),可以定义一个有如下阶段的pipeline
- multiline阶段:针对可能多行输出的异常信息,使用正则表达式匹配头行输出,并将余下行等待加入第一行
- regex阶段:对每一行日志使用正则表达式
- pack阶段:将regex识别到的各个部分重新打包为json
- timestamp阶段:修改时间戳格式
在得出这个最佳实践前,我绕了好多弯路,因此记录一些问题:
- 为什么使用pack而不使用template? 在网上查的一些配置文件中使用template定义一个json模板,并将regex阶段解析的正则结果填充到json模板中。这的确是个思路。但我在使用中碰到问题:msg中的一些字段带有"符号,会破坏json模板中的"引号配对从而导致最终输出的json格式错误
- 如何快速调试pipeline?
- 可以参考官方文档:https://grafana.org.cn/docs/loki/latest/send-data/promtail/troubleshooting/,提供了详细的指示
- 使用
promtail -config.file=myConfigFile.yaml -check-syntax
检测配置文件 - 拉取一小份生产环境日志到本地,使用dry-run模式检查每个阶段的输出。检查Promtail的输出匹配,出现错误时再仔细debug
cat my.log | promtail --stdin --dry-run --inspect --client.url http://127.0.0.1:3100/loki/api/v1/push
# 处理SpringBoot框架日志例子
针对一个使用spring框架的java输出日志,log输出格式定义为:
|
|
其中一个日志行的示例如下:
|
|
针对这个配置我使用的pipeline配置文件如下:
|
|
经过测试,无问题,正常匹配到loki后正常解析
# 其他
另外还有一个思路可行:使用SpringBoot中logback或log4j2的输出配置,在输出普通log日志时,再输出一份json格式的日志。这样promtail不再需要定义stage直接解析json就可以了。但这个方案对代码侵入性太高,需要改造大量模块的输出配置,对已上线的系统不建议使用。
# 总结
一开始在配置时照抄网上的一些配置文件,绕了很多弯路依然无法解析。最后还是重新看了官方文档一步一步慢慢调试得出了正确的结论。总结为平常还是太依赖别人的经验文档,应该更多看看官方文档,多思考怎么使用,减少抄别人配置的习惯
# 参考
参考&感谢文档