0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Spring Boot 3のアプリケーションログをGoogle Cloud Logging向けに最適化する

Posted at

はじめに

現在、Spring Boot3 x Google Cloudでアプリケーション開発を行っています。

Cloud Logging、便利ですよね。
アプリケーションで標準出力したログを拾ってよしなに表示してくれる。
image.png
ただ、そのままだと上記のようにErrorログも標準出力として表示されてしまっていたり、
タイムスタンプがログのPayloadにもそのまま出力されていたり、
いろいろと痒いところに手が届かないので、この辺をいい感じに整備したい。

構造化ロギング

Cloud Loggingは、特定のフィールドを含むJSONログを自動的にマッピングして読み込んでくれます。
https://cloud.google.com/logging/docs/structured-logging?hl=ja#special-payload-fields

例えば、JSONログ中のseverityフィールドにログの重要度(INFO、ERRORなど)を含めると、その重要度に応じてログを振り分けて表示してくれるようになります。

それ以外にも同一リクエストのログをグルーピングするのに利用されるtraceフィールドなど、含めておいた方が後々嬉しいフィールドがあります。

spring-cloud-gcp-starter-logging

Google CloudはSpring環境向けに様々なモジュールを提供しています。
https://spring.io/projects/spring-cloud-gcp

Cloud Logging向けのstarterモジュールもあるので、これを使っていきます。
https://mvnrepository.com/artifact/com.google.cloud/spring-cloud-gcp-starter-logging

build.gradle

依存を追加します。

build.gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'com.google.cloud:spring-cloud-gcp-starter-logging:4.5.0' // 追加
}

logback-spring.xml

spring-cloud-gcp-starter-logginglogback-json-appender.xmlが定義されているので、それを読み込みます。基本的にこれだけでOK

logback-spring.xml
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="com/google/cloud/spring/logging/logback-json-appender.xml"/>

    <root level="INFO">
        <appender-ref ref="CONSOLE_JSON"/>
    </root>
</configuration>

こいつをresources配下に置いてやるだけ

DemoApplication

DemoApplication.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
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;

@RestController
@SpringBootApplication
public class DemoApplication {

    @Autowired
    private DemoService demoService;

    @GetMapping("/")
    public void demo() {
        demoService.exec();
    }

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

}
DemoService.java
package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class DemoService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DemoService.class);

    public void exec() {
        LOGGER.info("This is INFO.");
        LOGGER.warn("This is WARNING.");
        LOGGER.error("This is ERROR.");
    }

}

デプロイしてログ確認

Severityが適切に反映されていていい感じですね!
image.png

起動時のロゴも消したい

application.properties(もしくはyaml)に以下を記述すればOK

application.properties
spring.main.banner-mode=off

traceを含める

Google Cloudでは、同一リクエストをグルーピングできるtraceを含めることができます。
spring-cloud-gcp-starter-loggingを使えば、こいつも自動的にログに含まれるようになります。便利。
image.png
「一致エントリを表示」を選択すると
image.png
同一リクエストで出力されたログを絞り込めます。
image.png

Asyncを使った場合のtrace

通常、Spring BootのAsync機能を使うと、traceが欠落してしまいます。
これを解決するには、micrometer-tracing-bridge-braveを利用して、AsyncExecutorをwrapしてあげます。

build.gradle
dependencies {
	implementation 'io.micrometer:micrometer-tracing-bridge-brave:1.1.2' // 追加
}
AsyncTraceContextConfig.java
package com.example.demo;

import io.micrometer.context.ContextExecutorService;
import io.micrometer.context.ContextSnapshotFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@EnableAsync
@Configuration(proxyBeanMethods = false)
public class AsyncTraceContextConfig implements AsyncConfigurer {

    private final ThreadPoolTaskExecutor taskExecutor;

    public AsyncTraceContextConfig(ThreadPoolTaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    @Override
    public Executor getAsyncExecutor() {
        return ContextExecutorService.wrap(taskExecutor.getThreadPoolExecutor(),
                () -> ContextSnapshotFactory.builder().build().captureAll());
    }

}

その他

以前は

この子たちを使っていたりもしたのですが、Google Cloud x Spring Bootに関してはspring-cloud-gcp-starter-loggingを使ってあげれば、あまり難しい設定も要らずに簡単にログ周りを整理できるので良いなと思いました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?