Grafana Loki笔记06: 使用Promtail收集Java应用日志发送给Loki
2023-05-28
前面学习了Promtail的基础知识和配置示例,本节做一个实战练习,使用Promtail收集Java应用日志发送给Loki。
这里假设的场景是一个Spring Boot的Java程序,日志框架使用了Logback。这个程序使用Logback将日志以文件形式写到磁盘上。 Logback的配置如下:
1<?xml version="1.0" encoding="UTF-8"?>
2<configuration debug="false" scan="false" scanPeriod="30 seconds">
3
4 <contextName>myapp-name</contextName>
5
6 <property name="logsPath" value="logs" />
7 <timestamp key="currentMonth" datePattern="yyyyMM" />
8
9 <appender name="FILE" class="ch.qos.logback.core.FileAppender">
10 <file>${logsPath}/${currentMonth}/myapp.log</file>
11 <encoder>
12 <charset>UTF-8</charset>
13 <pattern>
14 <![CDATA[
15 %d{yyyy-MM-dd HH:mm:ss.SSS,+00:00} [%t] ${SPRING_PROFILES_ACTIVE} %p %logger ${CONTEXT_NAME} - %m%n
16 ]]>
17 </pattern>
18 </encoder>
19 </appender>
20
21 <logger name="com.myapp" additivity="false" level="INFO">
22 <appender-ref ref="FILE" />
23 </logger>
24
25 <root level="WARN">
26 <appender-ref ref="FILE" />
27 </root>
28</configuration>
其中FileAppender
的encoder.pattern
决定了日志文件中每行日志的格式。处理java应用的日志是需要关注多行日志模式的,即一条日志可能由多行文本组成。例如:
12023-05-28 16:43:20
2[pool-8-thread-1] dev ERROR com.myapp.ProjectQuery myapp-name - query error
3org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
4 at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
5 at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
6 at com.sun.proxy.$Proxy108.selectOne(Unknown Source)
7 at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
8 at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:83)
9 at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
10...
针对这个例子,java应用使用logback将日志按照固定格式落盘,与应用在同一服务器节点上的Promtail将跟踪日志文件,解析日志并将日志发送到Loki。 promtail.yaml配置如下:
1server:
2 disable: true
3
4clients:
5- url: http://loki-write:3100/loki/api/v1/push
6 tenant_id: org1
7
8positions:
9 filename: /app/logs/positions.yaml
10
11target_config:
12 sync_period: 10s
13
14scrape_configs:
15- job_name: java_logs
16 static_configs:
17 - targets:
18 - localhost
19 labels:
20 job: java_logs
21 __path__: /app/logs/*/*.log
22 pipeline_stages:
23 - multiline:
24 firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'
25 max_lines: 256
26 max_wait_time: 5s
27 - regex:
28 expression: '^(?P<time>\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) (?P<message>\[(?P<thread>.*?)\] (?P<profileName>[^\s]+) (?P<level>[^\s]+) (?P<logger>[^\s]+) (?P<contextName>[^\s]+) - [\s\S]*)'
29 - labels:
30 contextName:
31 profileName:
32 level:
33 - timestamp:
34 source: time
35 format: '2006-01-02 15:04:05.999'
36 location: "UTC"
37 - drop:
38 older_than: 120h
39 drop_counter_reason: "line_too_old"
40 - labeldrop:
41 - filename
42 - output:
43 source: message
server.disable=true
将禁用Promtail的HTTP和gRPC服务监听
clients
块配置了Promtail如何连接到Loki的实例,配置了loki write的地址,以及使用的租户id
positions.filename
设置了Promtail读取日志文件时记录读取位置的文件
scrape_configs
中配置了一个job java_logs
,将跟踪匹配/app/logs/*/*.log
的日志,并为其配置了multiline
, regex
, labels
, timestamp
, drop
, labeldrop
, output
等7个 pipeline stage。
multiline
阶段的功能是将多行合并成一个多行块,然后将其传递到管道中的下一个阶段。通过firstline首行正则表达式来识别新的块。不匹配该表达式的任何行都被视为前一个匹配块的一部分。regex
阶段接受一个正则表达式,并提取捕获的命名分组,以便在后续阶段中使用。labels
阶段从提取到的map中获取数据,并修改将发送到Loki的日志条目的标签集合。这个例子中的配置将从前面regex阶段获取到contextName,profileName,level作为日志的label标记timestamp
阶段从提取的map中解析数据,并覆盖Loki存储的日志的最终时间值。如果没有这个阶段,Promtail将将日志条目的时间戳与读取该条目的时间关联起来。drop
阶段是一个筛选阶段,可以根据多个选项丢弃日志。这里配置的是丢弃120小时之前的日志。labeldrop
阶段从发送到Loki的日志条目的标签集合中删除标签,这里我们将filename这个标签删除了。output
阶段从提取的map中获取数据并更改将发送到Loki的日志行。