青蛙小白
博客 / 2023/05

Grafana Loki笔记06: 使用Promtail收集Java应用日志发送给Loki

2023/05/28 · — 字 · 阅读约 — 分钟 ·
目录

前面学习了Promtail的基础知识和配置示例,本节做一个实战练习,使用Promtail收集Java应用日志发送给Loki。

这里假设的场景是一个Spring Boot的Java程序,日志框架使用了Logback。这个程序使用Logback将日志以文件形式写到磁盘上。 Logback的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false" scanPeriod="30 seconds">

    <contextName>myapp-name</contextName>
    
    <property name="logsPath" value="logs" />
    <timestamp key="currentMonth" datePattern="yyyyMM" />

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
      <file>${logsPath}/${currentMonth}/myapp.log</file>
      <encoder>
        <charset>UTF-8</charset>
        <pattern>
          <![CDATA[
          %d{yyyy-MM-dd HH:mm:ss.SSS,+00:00} [%t] ${SPRING_PROFILES_ACTIVE} %p %logger ${CONTEXT_NAME} - %m%n
          ]]>
        </pattern>
      </encoder>
    </appender>

    <logger name="com.myapp" additivity="false" level="INFO">
      <appender-ref ref="FILE" />
    </logger>

    <root level="WARN">
      <appender-ref ref="FILE" />
    </root>
</configuration>

其中FileAppenderencoder.pattern决定了日志文件中每行日志的格式。处理java应用的日志是需要关注多行日志模式的,即一条日志可能由多行文本组成。例如:

2023-05-28 16:43:20	
[pool-8-thread-1] dev ERROR com.myapp.ProjectQuery myapp-name - query error
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
	at com.sun.proxy.$Proxy108.selectOne(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:83)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
...

针对这个例子,java应用使用logback将日志按照固定格式落盘,与应用在同一服务器节点上的Promtail将跟踪日志文件,解析日志并将日志发送到Loki。 promtail.yaml配置如下:

server:
  disable: true

clients:
- url: http://loki-write:3100/loki/api/v1/push
  tenant_id: org1

positions:
  filename: /app/logs/positions.yaml

target_config:
  sync_period: 10s

scrape_configs:
- job_name: java_logs
  static_configs:
  - targets:
      - localhost
    labels:
      job: java_logs
      __path__: /app/logs/*/*.log
  pipeline_stages:
  - multiline:
      firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'
      max_lines: 256
      max_wait_time: 5s
  - regex:
      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]*)'
  - labels:
      contextName:
      profileName:
      level:
  - timestamp:
      source: time
      format: '2006-01-02 15:04:05.999'
      location: "UTC"
  - drop:
      older_than: 120h
      drop_counter_reason: "line_too_old"
  - labeldrop:
    - filename
  -  output:
      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的日志行。
TAGS # loki
评论