Spring Boot Actuator の logback_events_total メトリクス値

## 概要

- Spring Boot Actuator の prometheus エンドポイントが出力する logback_events_total についてまとめる
- Spring Boot Actuator と Micrometer Registry Prometheus で logback_events_total を出力する

## logback_events_total とは

- Spring Boot Actuator が出力するメトリクス値
- 該当するログレベルのログ出力回数を表す
- Prometheus の Counter タイプ

Counter タイプは単調増加する値を表す累積メトリクス値で、増加はするが減少はせず、再起動時にゼロにリセットされる。

[Metric types \| Prometheus](https://prometheus.io/docs/concepts/metric_types/)

> A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart. For example, you can use a counter to represent the number of requests served, tasks completed, or errors.
> 
> Do not use a counter to expose a value that can decrease. For example, do not use a counter for the number of currently running processes; instead use a gauge.

## logback_events_total の出力例

数値はそのログレベルのログ出力回数を表す。

```
# HELP logback_events_total Number of error level events that made it to the logs
# TYPE logback_events_total counter
logback_events_total{level="warn",} 2.0
logback_events_total{level="debug",} 0.0
logback_events_total{level="error",} 2.0
logback_events_total{level="trace",} 0.0
logback_events_total{level="info",} 0.0
```

## サンプルコード

環境: Java 11 + Spring Boot 2.2.7 + Micrometer Registry Prometheus 1.3.9 + Gradle 6.4

### ファイル一覧

```
├── build.gradle
├── settings.gradle
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── MyApplication.java
        └── resources
            └── application.properties
```

### build.gradle

Spring Boot Actuator を使用するため Spring Boot Actuator Starter を指定する。
Prometheus 用の出力をするため Micrometer Registry Prometheus を指定する。

```groovy
plugins {
  id 'org.springframework.boot' version '2.2.7.RELEASE'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  // Spring Boot Web Starter
  implementation 'org.springframework.boot:spring-boot-starter-web'
  // Spring Boot Actuator Starter
  implementation 'org.springframework.boot:spring-boot-starter-actuator'
  // Micrometer Registry Prometheus
  implementation 'io.micrometer:micrometer-registry-prometheus:1.3.9'
}
```

ロギングライブラリの設定はデフォルトを使用する。

[Spring Boot の機能 \- 公式ドキュメントの日本語訳](https://spring.pleiades.io/spring-boot/docs/2.2.7.RELEASE/reference/html/spring-boot-features.html#boot-features-logging)

> デフォルトでは、「スターター」を使用する場合、Logback がロギングに使用されます。Java Util Logging、Commons Logging、Log4J、または SLF4J を使用する依存ライブラリがすべて正常に機能するように、適切な Logback ルーティングも含まれています。

### settings.gradle

```groovy
rootProject.name = 'my-app'
```

### src/main/resources/application.properties

```application.properties
# ログ出力レベルを指定
logging.level.root=warn
logging.level.org.springframework.web=warn
logging.level.org.hibernate=error

# サンプルアプリケーションは warn レベル以上のログを出力する
logging.level.com.example=warn

# Spring Boot Actuator のすべてのエンドポイントをいったん無効にする設定
management.endpoints.enabled-by-default=false

# Prometheus 用のエンドポイントを有効にする設定
management.endpoint.prometheus.enabled=true
management.endpoints.web.exposure.include=prometheus
```

### src/main/java/com/example/MyApplication.java

```java
package com.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@SpringBootApplication
@RestController
public class MyApplication {

  // SLF4J Logger
  private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
  }

  @GetMapping("/")
  public Map index() {
    // アクセスが来たらそれぞれのログレベルでログを出力する
    logger.trace("Sample TRACE Message");
    logger.debug("Sample DEBUG Message");
    logger.info ("Sample INFO  Message");
    logger.warn ("Sample WARN  Message");
    logger.error("Sample ERROR Message");
    return Map.of("message", "Hello, world.");
  }
}
```

## サンプル実行例

Gradle の bootRun タスクで Spring Boot アプリケーションを起動。

```
$ gradle bootRun

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.7.RELEASE)
```

別のターミナルから curl で Spring Boot Actuator の HTTP エンドポイント /actuator/prometheus にアクセスする。
どのログレベルもログがまだ0件なので値が「0.0」になっている。

```
$ curl --silent http://localhost:8080/actuator/prometheus | grep logback
# HELP logback_events_total Number of error level events that made it to the logs
# TYPE logback_events_total counter
logback_events_total{level="warn",} 0.0
logback_events_total{level="debug",} 0.0
logback_events_total{level="error",} 0.0
logback_events_total{level="trace",} 0.0
logback_events_total{level="info",} 0.0
```

curl でトップページにアクセスする。

```
$ curl --silent http://localhost:8080/
{"message":"Hello, world."}
```

Spring Boot がログを出力する。
application.properties で warn レベルを指定しているので warn と error のログが出力される。

```
2020-05-11 20:51:43.225  WARN 70007 --- [nio-8080-exec-4] com.example.MyApplication                : Sample WARN  Message
2020-05-11 20:51:43.233 ERROR 70007 --- [nio-8080-exec-4] com.example.MyApplication                : Sample ERROR Message
```

curl で /actuator/prometheus にアクセスする。
warn と error のログが1つずつ出力されたので該当するログレベルの項目の値が「1.0」になる。

```
$ curl --silent http://localhost:8080/actuator/prometheus | grep logback
# HELP logback_events_total Number of error level events that made it to the logs
# TYPE logback_events_total counter
logback_events_total{level="warn",} 1.0
logback_events_total{level="debug",} 0.0
logback_events_total{level="error",} 1.0
logback_events_total{level="trace",} 0.0
logback_events_total{level="info",} 0.0
```

再度 curl でトップページにアクセスする。

```
$ curl --silent http://localhost:8080/
{"message":"Hello, world."}
```

Spring Boot がログを出力する。

```
2020-05-11 20:52:09.924  WARN 70007 --- [nio-8080-exec-8] com.example.MyApplication                : Sample WARN  Message
2020-05-11 20:52:09.924 ERROR 70007 --- [nio-8080-exec-8] com.example.MyApplication                : Sample ERROR Message
```

curl で /actuator/prometheus にアクセスする。
warn と error のログが追加で1つずつ出力されたので該当するログレベルの項目の値が「2.0」になる。

```
$ curl --silent http://localhost:8080/actuator/prometheus | grep logback
# HELP logback_events_total Number of error level events that made it to the logs
# TYPE logback_events_total counter
logback_events_total{level="warn",} 2.0
logback_events_total{level="debug",} 0.0
logback_events_total{level="error",} 2.0
logback_events_total{level="trace",} 0.0
logback_events_total{level="info",} 0.0
```

## 参考資料

- [Spring Boot Actuator: 本番対応機能 \- ドキュメント](https://spring.pleiades.io/spring-boot/docs/2.2.7.RELEASE/reference/html/production-ready-features.html)
- [Micrometer Application Monitoring](https://micrometer.io/docs/registry/prometheus)
- [Spring Boot の機能 4. ロギング \- 公式ドキュメントの日本語訳](https://spring.pleiades.io/spring-boot/docs/2.2.7.RELEASE/reference/html/spring-boot-features.html#boot-features-logging)
- [Spring でのロギング](https://cloud.ibm.com/docs/java?topic=java-spring-logging&locale=ja)