5

在Spring Boot中用Loki实现日志记录

 1 year ago
source link: https://www.jdon.com/67246.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在Spring Boot中用Loki实现日志记录

在本文中,您将了解如何收集 Spring Boot 应用程序日志并将其发送到Grafana Loki。我们将使用附加的Loki4j Logback。

Loki 是一个受 Prometheus 启发的水平可扩展、高可用的日志聚合系统。

本文展示如何配置应用程序和 Loki 之间的集成。但是,可以使用自动配置库来记录 HTTP 请求和响应,这将为您完成所有这些步骤。

克隆 GitHub 存储库

使用 Loki4j Logback Appender
为了使用Appender Loki4j Logback,我们需要在 Maven 中包含一个依赖项pom.xml。该库的当前版本是1.4.1:

<dependency>
    <groupId>com.github.loki4j</groupId>
    <artifactId>loki-logback-appender</artifactId>
    <version>1.4.1</version>
</dependency>

然后我们需要在 src/main/resources 目录下创建 logback-spring.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <springProperty name="name" source="spring.application.name" />

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>
        %d{HH:mm:ss.SSS} %-5level %logger{36} %X{X-Request-ID} - %msg%n
      </pattern>
    </encoder>
  </appender>

  <appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
    <!-- (1) -->
    <http>
      <url>http://localhost:3100/loki/api/v1/push</url>
    </http>
    <format>
      <!-- (2) -->
      <label>
        <pattern>app=${name},host=${HOSTNAME},level=%level</pattern>
        <!-- (3) -->
        <readMarkers>true</readMarkers>
      </label>
      <message>
        <!-- (4) -->
        <pattern>
{
   "level":"%level",
   "class":"%logger{36}",
   "thread":"%thread",
   "message": "%message",
   "requestId": "%X{X-Request-ID}"
}
        </pattern>
      </message>
    </format>
  </appender>

  <root level="INFO">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="LOKI" />
  </root>

</configuration>

这样我们的Loki实例在http://localhost:3100 下可访问。

Loki并不对日志的内容进行索引--而只是对元数据标签进行索引。

  1. 有一些静态标签,如应用程序名称、日志级别或主机名。见上面logback-spring.xml第1行注释处
  2. 我们可以在format.label字段中设置它们(2)。
  3. 我们还将设置一些动态标签,因此我们启用Logback标记功能(3)。
  4. 最后,我们要设置日志格式模式(4)。为了简化,与LogQL(Loki查询语言)的潜在转换,我们将使用JSON符号。

除了静态标签,我们还可以发送动态数据:
例如,只针对当前请求的特定内容。假设我们有一个管理人的服务,我们想从请求中记录目标人的ID。

@RestController
@RequestMapping("/persons")
public class PersonController {

    private final Logger LOG = LoggerFactory
       .getLogger(PersonController.class);
    private final List<Person> persons = new ArrayList<>();

    @GetMapping
    public List<Person> findAll() {
        return persons;
    }

    @GetMapping("/{id}")
    public Person findById(@PathVariable("id") Long id) {
        Person p = persons.stream().filter(it -> it.getId().equals(id))
                .findFirst()
                .orElseThrow();
        LabelMarker marker = LabelMarker.of("personId", () -> 
           String.valueOf(p.getId())); [b]// (1)[/b]
        LOG.info(marker, "Person successfully found"); [b]// (2)[/b]
        return p;
    }

    @GetMapping("/name/{firstName}/{lastName}")
    public List<Person> findByName(
       @PathVariable("firstName") String firstName,
       @PathVariable("lastName") String lastName) {
       
       return persons.stream()
          .filter(it -> it.getFirstName().equals(firstName)
                        && it.getLastName().equals(lastName))
          .toList();
    }

    @PostMapping
    public Person add(@RequestBody Person p) {
        p.setId((long) (persons.size() + 1));
        LabelMarker marker = LabelMarker.of("personId", () -> 
           String.valueOf(p.getId()));
        LOG.info(marker, "New person successfully added");
        persons.add(p);
        return p;
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable("id") Long id) {
        Person p = persons.stream()
           .filter(it -> it.getId().equals(id))
           .findFirst()
           .orElseThrow();
        persons.remove(p);
        LabelMarker marker = LabelMarker.of("personId", () -> 
           String.valueOf(id));
        LOG.info(marker, "Person successfully removed");
    }

    @PutMapping
    public void update(@RequestBody Person p) {
        Person person = persons.stream()
                .filter(it -> it.getId().equals(p.getId()))
                .findFirst()
                .orElseThrow();
        persons.set(persons.indexOf(person), p);
        LabelMarker marker = LabelMarker.of("personId", () -> 
            String.valueOf(p.getId()));
        LOG.info(marker, "Person successfully updated");
    }

}

正如我之前提到的,使用Loki4j我们可以使用Logback标记。

在经典的Logback中,标记主要用于过滤日志记录。
使用Loki,我们只需要定义 LabelMarker 对象,其中包含动态字段的键/值图,代码在(1)处;然后我们将该对象传递给当前的日志行(2)。

假设我们在单个日志行中有多个动态字段,我们必须以这种方式创建LabelMarker对象:

LabelMarker marker = LabelMarker.of(() -> Map.of("audit", "true",
                    "X-Request-ID", MDC.get("X-Request-ID"),
                    "X-Correlation-ID", MDC.get("X-Correlation-ID")));

用Spring Boot应用程序运行Loki
在本地机器上运行Loki的最简单方法是使用Docker容器。除了Loki实例,我们还将运行Grafana来显示和搜索日志。下面是包含所有必要服务的docker-compose.yml。你可以用docker compose up命令来运行它们。

version: "3"

networks:
  loki:

services:
  loki:
    image: grafana/loki:2.8.2
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    networks:
      - loki

  grafana:
    environment:
      - GF_PATHS_PROVISIONING=/etc/grafana/provisioning
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
    entrypoint:
      - sh
      - -euc
      - |
        mkdir -p /etc/grafana/provisioning/datasources
        cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
        apiVersion: 1
        datasources:
        - name: Loki
          type: loki
          access: proxy
          orgId: 1
          url: http://loki:3100
          basicAuth: false
          isDefault: true
          version: 1
          editable: false
        EOF
        /run.sh
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    networks:
      - loki

为了利用Spring Boot的Docker Compose支持,我们需要将docker-compose.yml放在应用根目录下。然后,我们必须在Maven的pom.xml中包含spring-boot-docker-compose依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-docker-compose</artifactId>
  <optional>true</optional>
</dependency>

运行:

$ mvn spring-boot:run

使用Spring Boot Loki Starter库
如果你不想自己配置上面这些,你可以使用我的Spring Boot库,它提供了自动配置功能。此外,它还自动记录所有传入的HTTP请求和传出的HTTP响应。如果默认设置已经足够了,你只需要把单一的Spring Boot启动器作为一个依赖项:

<dependency>
  <groupId>com.github.piomin</groupId>
  <artifactId>logstash-logging-spring-boot-starter</artifactId>
  <version>2.0.2</version>
</dependency>

该库用几个默认标签记录每个请求和响应,包括例如requestId或correlationId。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK