これは、なにをしたくて書いたもの?
HTTPリクエスト/レスポンス(クライアント、サーバー)のログ出力をしてくれる、Logbookというライブラリーがあります。
1度試しておきたかったので、今回はSpring Bootで試してみることにしました。
Logbook
GitHub - zalando/logbook: An extensible Java library for HTTP request and response logging
機能としては以下になっています。
- ログ出力
- カスタマイズ
- ログ出力、フォーマット、ログ出力の条件
- 多数のフレームワーク、HTTPクライアントライブラリーのサポート
- センシティブなデータの難読化
- Spring Boot Auto Configuration
- Scalyr互換
- 理にかなったデフォルト値
サポートしているフレームワークやライブラリーはこのあたりです。
- Apache HttpClient 4.x、5.x
- JAX-RS 2、3
- Netty 4
- OkHttp 2.x、3.x
- Spring Framework 5.x、6.x
- Spring Boot 2.x、3.x
- Ktor
- logstash-logback-encoder
JAX-RS 3.x、Spring Boot 3.xおよびSpring Framework 6.xを使う場合はJava 17以降が必要で、それ以外はJava 8以降で使用できます。
基本的な使い方はこちらを参照。
設定はLogbook
のインスタンスに対して行います。
ログの記録戦略、リクエストやレスポンスからの属性の抽出、出力する際の条件指定・フィルタリング・書式設定なども行えます。
ログ出力自体は、デフォルトではSLF4Jを使って行われますが、こちらもカスタマイズできます。
ログレベルはTRACEで指定されているので、少なくともLogbookのログに関してはTRACEレベル以上を出力するように設定する必要が
あります。
今回はSpring Boot 3でこのLogbookを試していきます。
Spring BootでLogbookを使う
Spring FrameworkやSpring Bootで使う場合のドキュメントはこちら。Spring Bootで使う場合は、Starterを利用することになります。
ちなみに、Spring Framework 5.xやSpring Boot 2.xで使う時はclassifier
がjavax
のlogbook-servletを追加する必要があるようです。
<dependency> <groupId>org.zalando</groupId> <artifactId>logbook-servlet</artifactId> <version>${logbook.version}</version> <classifier>javax</classifier> </dependency>
Logbook Auto ConfigurationではLogbook
のインスタンスが自動構成されます。また、必要に応じてフィルターなどBean定義することで
カスタマイズも容易そうです。
設定できるプロパティーも一覧化されています。
Logbook / Usage / Spring Boot Starter / Configuration
今回はあまりカスタマイズなど考えずに単純に組み込んでみます。
Webアプリケーションを2つ作成し、以下のような構成にしてみます。
flowchart LR クライアント --> |curl/HTTP| A A[API-A/Spring Boot] --> |RestClient/HTTP| B[API-B/Spring Boot]
この時、@RestController
から見たリクエスト/レスポンスの記録、RestClient
から見たリクエスト/レスポンスの記録を行います。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.3 2024-04-16 OpenJDK Runtime Environment (build 21.0.3+9-Ubuntu-1ubuntu122.04.1) OpenJDK 64-Bit Server VM (build 21.0.3+9-Ubuntu-1ubuntu122.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.9.7 (8b094c9513efc1b9ce2d952b3b9c8eaedaf8cbf0) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.3, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-112-generic", arch: "amd64", family: "unix"
API-Bを作成する
まずは奥にあるAPI-Bから作成します。
Spring Initializrで、依存関係にweb
を指定してプロジェクトを作成。
$ curl -s https://start.spring.io/starter.tgz \ -d bootVersion=3.3.0 \ -d javaVersion=21 \ -d type=maven-project \ -d name=api-b \ -d groupId=org.littlewings \ -d artifactId=api-b \ -d version=0.0.1-SNAPSHOT \ -d packageName=org.littlewings.spring.logbook.b \ -d dependencies=web \ -d baseDir=api-b | tar zxvf -
ディレクトリ内に移動。
$ cd api-b
生成されたソースコードは削除しておきます。
$ rm src/main/java/org/littlewings/spring/logbook/b/ApiBApplication.java src/test/java/org/littlewings/spring/logbook/b/ApiBApplicationTests.java
Maven依存関係はこのような状態になっていました。
<properties> <java.version>21</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
また、application.properties
はこういう状態です。
src/main/resources/application.properties
spring.application.name=api-b
まずは依存関係にlogbook-spring-boot-starterを追加します。
<dependency> <groupId>org.zalando</groupId> <artifactId>logbook-spring-boot-starter</artifactId> <version>3.9.0</version> </dependency>
単純な@RestController
を作成。
src/main/java/org/littlewings/spring/logbook/b/HelloController.java
package org.littlewings.spring.logbook.b; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/b/hello") @RestController public class HelloController { @GetMapping public String message(@RequestParam(required = false) String word) { if (word != null) { return String.format("Hello %s!!", word); } else { return "Hello World!!"; } } }
mainメソッドを持ったクラスを作成。
src/main/java/org/littlewings/spring/logbook/b/App.java
package org.littlewings.spring.logbook.b; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String... args) { SpringApplication.run(App.class, args); } }
ここで、application.properties
を変更しLogbookに関するログレベルをTRACEに変更します。
src/main/resources/application.properties
spring.application.name=api-b server.port=9080 logging.level.org.zalando.logbook=TRACE
また、今後のためにポートも変更しておきました。
これでいったん起動。
$ mvn spring-boot:run
アクセスしてみます。
$ curl localhost:9080/b/hello Hello World!!
これだけでリクエストとレスポンスのログが記録されました。
2024-06-09T00:03:17.198+09:00 TRACE 42419 --- [api-b] [nio-9080-exec-1] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"a85396154ef9a9c3","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:9080/b/hello","host":"localhost","path":"/b/hello","scheme":"http","port":"9080","headers":{"accept":["*/*"],"host":["localhost:9080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:03:17.243+09:00 TRACE 42419 --- [api-b] [nio-9080-exec-1] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"a85396154ef9a9c3","duration":100,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Length":["13"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:03:17 GMT"]},"body":"Hello World!!"}
フォーマットはJSONですね。
このフォーマットはHTTP、JSON、Common Log Format(CLF)、Extended Log Format(ELF)、cURL、Splunkから選べるようです。
Logbook / Usage / Phases / Formatting
QueryStringも入れてみましょう。
$ curl localhost:9080/b/hello?word=LogBook Hello LogBook!!
ログ。
2024-06-09T00:05:56.753+09:00 TRACE 42419 --- [api-b] [nio-9080-exec-2] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"db09731279cf521b","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:9080/b/hello?word=LogBook","host":"localhost","path":"/b/hello","scheme":"http","port":"9080","headers":{"accept":["*/*"],"host":["localhost:9080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:05:56.756+09:00 TRACE 42419 --- [api-b] [nio-9080-exec-2] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"db09731279cf521b","duration":4,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Length":["15"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:05:56 GMT"]},"body":"Hello LogBook!!"}
よさそうですね。
もうひとつ@RestController
を追加してみましょう。
src/main/java/org/littlewings/spring/logbook/b/CalcController.java
package org.littlewings.spring.logbook.b; import org.springframework.web.bind.annotation.*; @RequestMapping("/b/calc") @RestController public class CalcController { @GetMapping public CalcResponse get(CalcRequest request) { return new CalcResponse(request.a() + request.b()); } @PostMapping public CalcResponse post(@RequestBody CalcRequest request) { return new CalcResponse(request.a() + request.b()); } public record CalcRequest(int a, int b) { } public record CalcResponse(int result) { } }
1度停止して起動。
$ mvn spring-boot:run
GET。
$ curl 'localhost:9080/b/calc?a=3&b=5' {"result":8}
ログ。
2024-06-09T00:07:36.025+09:00 TRACE 42814 --- [api-b] [nio-9080-exec-1] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"e07c573c5790e411","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:9080/b/calc?a=3&b=5","host":"localhost","path":"/b/calc","scheme":"http","port":"9080","headers":{"accept":["*/*"],"host":["localhost:9080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:07:36.098+09:00 TRACE 42814 --- [api-b] [nio-9080-exec-1] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"e07c573c5790e411","duration":110,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Type":["application/json"],"Date":["Sat, 08 Jun 2024 15:07:36 GMT"],"Transfer-Encoding":["chunked"]},"body":{"result":8}}
POST。
$ curl -XPOST -H 'Content-Type: application/json' localhost:9080/b/calc -d '{"a": 5, "b": 3}' {"result":8}
ログ。
2024-06-09T00:08:27.818+09:00 TRACE 42814 --- [api-b] [nio-9080-exec-2] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"e2c540ee7c0b6ed0","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"POST","uri":"http://localhost:9080/b/calc","host":"localhost","path":"/b/calc","scheme":"http","port":"9080","headers":{"accept":["*/*"],"content-length":["16"],"content-type":["application/json"],"host":["localhost:9080"],"user-agent":["curl/7.81.0"]},"body":{"a":5,"b":3}} 2024-06-09T00:08:27.857+09:00 TRACE 42814 --- [api-b] [nio-9080-exec-2] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"e2c540ee7c0b6ed0","duration":39,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Type":["application/json"],"Date":["Sat, 08 Jun 2024 15:08:27 GMT"],"Transfer-Encoding":["chunked"]},"body":{"result":8}}
これでAPI-Bの準備は完了しました。
API-Aを作成する
次は手前にあるAPI-Aを作成します。
このREST APIは、API-Bへリクエストを転送するプロキシの役割になります。HTTPリクエストはRestClient
を使って行います。
Spring Bootプロジェクトの作成。依存関係はこちらもweb
のみです。
$ curl -s https://start.spring.io/starter.tgz \ -d bootVersion=3.3.0 \ -d javaVersion=21 \ -d type=maven-project \ -d name=api-a \ -d groupId=org.littlewings \ -d artifactId=api-a \ -d version=0.0.1-SNAPSHOT \ -d packageName=org.littlewings.spring.logbook.a \ -d dependencies=web \ -d baseDir=api-a | tar zxvf -
ディレクトリ内へ移動。
$ cd api-a
生成されたソースコードは削除しておきます。
$ rm src/main/java/org/littlewings/spring/logbook/a/ApiAApplication.java src/test/java/org/littlewings/spring/logbook/a/ApiAApplicationTests.java
Maven依存関係など。
<properties> <java.version>21</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
application.properties
の内容。
src/main/resources/application.properties
spring.application.name=api-a
API-Bと同じようにlogbook-spring-boot-starterを依存関係に追加しておきます。
<dependency> <groupId>org.zalando</groupId> <artifactId>logbook-spring-boot-starter</artifactId> <version>3.9.0</version> </dependency>
まずはAPI-BのHelloController
にリクエストを転送する@RestController
を作成。
src/main/java/org/littlewings/spring/logbook/a/HelloController.java
package org.littlewings.spring.logbook.a; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestClient; import org.springframework.web.util.UriComponentsBuilder; import org.zalando.logbook.spring.LogbookClientHttpRequestInterceptor; @RequestMapping("/a/hello") @RestController public class HelloController { private RestClient restClient; public HelloController(RestClient.Builder restClientBuilder, LogbookClientHttpRequestInterceptor logbookClientHttpRequestInterceptor) { this.restClient = restClientBuilder .baseUrl("http://localhost:9080") .requestInterceptor(logbookClientHttpRequestInterceptor) .build(); } @GetMapping public String message(@RequestParam(required = false) String word) { if (word != null) { return restClient .get() .uri(UriComponentsBuilder.fromPath("/b/hello").queryParam("word", word).build().toUriString()) .retrieve() .body(String.class); } else { return restClient.get().uri("/b/hello").retrieve().body(String.class); } } }
ポイントは、RestClient.Builder#requestInterceptor
にLogbookのLogbookClientHttpRequestInterceptor
を設定していることです。
public HelloController(RestClient.Builder restClientBuilder, LogbookClientHttpRequestInterceptor logbookClientHttpRequestInterceptor) { this.restClient = restClientBuilder .baseUrl("http://localhost:9080") .requestInterceptor(logbookClientHttpRequestInterceptor) .build(); }
LogbookのドキュメントではRestTemplate
の例のみが載っていますが、こちらではRestTemplateBuilder#additionalInterceptors
で
設定することになっています。
private final RestTemplate restTemplate; MyClient(RestTemplateBuilder builder, LogbookClientHttpRequestInterceptor interceptor){ this.restTemplate = builder .additionalInterceptors(interceptor) .build(); }
Logbook / Usage / Spring Boot Starter / Autoconfigured beans from logbook-spring
mainメソッドを持ったクラス。
src/main/java/org/littlewings/spring/logbook/a/App.java
package org.littlewings.spring.logbook.a; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String... args) { SpringApplication.run(App.class, args); } }
application.properties
でLogbookのログレベルをTRACEにします。
src/main/resources/application.properties
spring.application.name=api-a logging.level.org.zalando.logbook=TRACE
この状態で、1度起動。
$ mvn spring-boot:run
リクエストを送ってみます。
$ curl localhost:8080/a/hello Hello World!!
記録されたログ。
2024-06-09T00:17:28.832+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"9423e7b7ce9a0a2b","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:8080/a/hello","host":"localhost","path":"/a/hello","scheme":"http","port":"8080","headers":{"accept":["*/*"],"host":["localhost:8080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:17:28.891+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"local","type":"request","correlation":"900de281a3b2cba1","protocol":"HTTP/1.1","remote":"localhost","method":"GET","uri":"http://localhost:9080/b/hello","host":"localhost","path":"/b/hello","scheme":"http","port":"9080","headers":{"Content-Length":["0"]}} 2024-06-09T00:17:28.926+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"remote","type":"response","correlation":"900de281a3b2cba1","duration":31,"protocol":"HTTP/1.1","status":200,"headers":{"Connection":["keep-alive"],"Content-Length":["13"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:17:28 GMT"],"Keep-Alive":["timeout=60"]},"body":"Hello World!!"} 2024-06-09T00:17:28.942+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"9423e7b7ce9a0a2b","duration":161,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Length":["13"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:17:28 GMT"]},"body":"Hello World!!"}
API-Aで受信したHTTPリクエスト → API-Bへ送信するHTTPリクエスト → API-Bから受信したHTTPレスポンス → API-AのHTTPレスポンス、
という順で並んでいます。
もちろんこの時、API-B側にもログが記録されています。
2024-06-09T00:17:28.910+09:00 TRACE 42814 --- [api-b] [nio-9080-exec-3] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"850715723543613a","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:9080/b/hello","host":"localhost","path":"/b/hello","scheme":"http","port":"9080","headers":{"accept":["*/*"],"connection":["keep-alive"],"host":["localhost:9080"],"user-agent":["Java/21.0.3"]}} 2024-06-09T00:17:28.915+09:00 TRACE 42814 --- [api-b] [nio-9080-exec-3] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"850715723543613a","duration":3,"protocol":"HTTP/1.1","status":200,"headers":{"Connection":["keep-alive"],"Content-Length":["13"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:17:28 GMT"],"Keep-Alive":["timeout=60"]},"body":"Hello World!!"}
以降はAPI-B側のログは割愛します。
QueryStringをつけてアクセス。
$ curl localhost:8080/a/hello?word=Logbook Hello Logbook!!
ログ。
2024-06-09T00:19:38.563+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"e14e787d34dcb37b","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:8080/a/hello?word=Logbook","host":"localhost","path":"/a/hello","scheme":"http","port":"8080","headers":{"accept":["*/*"],"host":["localhost:8080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:19:38.565+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"local","type":"request","correlation":"830c45c9cd75a33a","protocol":"HTTP/1.1","remote":"localhost","method":"GET","uri":"http://localhost:9080/b/hello?word=Logbook","host":"localhost","path":"/b/hello","scheme":"http","port":"9080","headers":{"Content-Length":["0"]}} 2024-06-09T00:19:38.573+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"remote","type":"response","correlation":"830c45c9cd75a33a","duration":7,"protocol":"HTTP/1.1","status":200,"headers":{"Connection":["keep-alive"],"Content-Length":["15"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:19:38 GMT"],"Keep-Alive":["timeout=60"]},"body":"Hello Logbook!!"} 2024-06-09T00:19:38.575+09:00 TRACE 43653 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"e14e787d34dcb37b","duration":13,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Length":["15"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:19:38 GMT"]},"body":"Hello Logbook!!"}
OKですね。
次はAPI-Bの@RestController
向けのクラスを追加します。
なのですが、またLogbookClientHttpRequestInterceptor
を各クラスで設定するのもめんどうです。
ここはRestClientCustomizer
で設定することにします。
$ cat src/main/java/org/littlewings/spring/logbook/a/RestClientConfig.java
package org.littlewings.spring.logbook.a; import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.zalando.logbook.spring.LogbookClientHttpRequestInterceptor; @Configuration public class RestClientConfig { @Bean public RestClientCustomizer restClientCustomizer(LogbookClientHttpRequestInterceptor logbookClientHttpRequestInterceptor) { return restClientBuilder -> restClientBuilder.requestInterceptor(logbookClientHttpRequestInterceptor); } }
HelloController
からはLogbookClientHttpRequestInterceptor
を追加するコードを削除します。
/* public HelloController(RestClient.Builder restClientBuilder, LogbookClientHttpRequestInterceptor logbookClientHttpRequestInterceptor) { this.restClient = restClientBuilder .baseUrl("http://localhost:9080") .requestInterceptor(logbookClientHttpRequestInterceptor) .build(); } */ public HelloController(RestClient.Builder restClientBuilder) { this.restClient = restClientBuilder .baseUrl("http://localhost:9080") .build(); }
API-BのCalcController
に対する@RestController
を追加。
src/main/java/org/littlewings/spring/logbook/a/CalcController.java
package org.littlewings.spring.logbook.a; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestClient; import org.springframework.web.util.UriComponentsBuilder; @RequestMapping("/a/calc") @RestController public class CalcController { private RestClient restClient; public CalcController(RestClient.Builder restClientBuilder) { this.restClient = restClientBuilder .baseUrl("http://localhost:9080") .build(); } @GetMapping public CalcResponse get(CalcRequest request) { return restClient .get() .uri(UriComponentsBuilder.fromPath("/b/calc").queryParam("a", request.a()).queryParam("b", request.b()).build().toUriString()) .retrieve() .body(CalcResponse.class); } @PostMapping public CalcResponse post(@RequestBody CalcRequest request) { return restClient .post() .uri("/b/calc") .body(request) .retrieve() .body(CalcResponse.class); } public record CalcRequest(int a, int b) { } public record CalcResponse(int result) { } }
アプリケーションを1度停止して、再度起動。
$ mvn spring-boot:run
まずは修正後の@RestController
から確認してみましょう。
$ curl localhost:8080/a/hello Hello World!!
ログ。
2024-06-09T00:24:01.780+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"a983b2ad61830d0f","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:8080/a/hello","host":"localhost","path":"/a/hello","scheme":"http","port":"8080","headers":{"accept":["*/*"],"host":["localhost:8080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:24:01.834+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"local","type":"request","correlation":"96c944673b11094f","protocol":"HTTP/1.1","remote":"localhost","method":"GET","uri":"http://localhost:9080/b/hello","host":"localhost","path":"/b/hello","scheme":"http","port":"9080","headers":{"Content-Length":["0"]}} 2024-06-09T00:24:01.863+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"remote","type":"response","correlation":"96c944673b11094f","duration":25,"protocol":"HTTP/1.1","status":200,"headers":{"Connection":["keep-alive"],"Content-Length":["13"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:24:01 GMT"],"Keep-Alive":["timeout=60"]},"body":"Hello World!!"} 2024-06-09T00:24:01.881+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-1] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"a983b2ad61830d0f","duration":179,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Length":["13"],"Content-Type":["text/plain;charset=UTF-8"],"Date":["Sat, 08 Jun 2024 15:24:01 GMT"]},"body":"Hello World!!"}
OKですね。
次は追加した@RestController
です。
GET。
$ curl 'localhost:8080/a/calc?a=5&b=3' {"result":8}
ログ。
2024-06-09T00:25:02.569+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"c9c87ff18eaf784a","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"GET","uri":"http://localhost:8080/a/calc?a=5&b=3","host":"localhost","path":"/a/calc","scheme":"http","port":"8080","headers":{"accept":["*/*"],"host":["localhost:8080"],"user-agent":["curl/7.81.0"]}} 2024-06-09T00:25:02.579+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"local","type":"request","correlation":"afae7f506d56ce6c","protocol":"HTTP/1.1","remote":"localhost","method":"GET","uri":"http://localhost:9080/b/calc?a=5&b=3","host":"localhost","path":"/b/calc","scheme":"http","port":"9080","headers":{"Content-Length":["0"]}} 2024-06-09T00:25:02.597+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"remote","type":"response","correlation":"afae7f506d56ce6c","duration":7,"protocol":"HTTP/1.1","status":200,"headers":{"Connection":["keep-alive"],"Content-Type":["application/json"],"Date":["Sat, 08 Jun 2024 15:25:02 GMT"],"Keep-Alive":["timeout=60"],"Transfer-Encoding":["chunked"]},"body":{"result":8}} 2024-06-09T00:25:02.646+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-2] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"c9c87ff18eaf784a","duration":78,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Type":["application/json"],"Date":["Sat, 08 Jun 2024 15:25:02 GMT"],"Transfer-Encoding":["chunked"]},"body":{"result":8}}
POST。
$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/a/calc -d '{"a": 5, "b": 3}' {"result":8}
ログ。
2024-06-09T00:25:59.111+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-6] org.zalando.logbook.Logbook : {"origin":"remote","type":"request","correlation":"f61193f865f23243","protocol":"HTTP/1.1","remote":"127.0.0.1","method":"POST","uri":"http://localhost:8080/a/calc","host":"localhost","path":"/a/calc","scheme":"http","port":"8080","headers":{"accept":["*/*"],"content-length":["16"],"content-type":["application/json"],"host":["localhost:8080"],"user-agent":["curl/7.81.0"]},"body":{"a":5,"b":3}} 2024-06-09T00:25:59.118+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-6] org.zalando.logbook.Logbook : {"origin":"local","type":"request","correlation":"91068428bef1d88e","protocol":"HTTP/1.1","remote":"localhost","method":"POST","uri":"http://localhost:9080/b/calc","host":"localhost","path":"/b/calc","scheme":"http","port":"9080","headers":{"Content-Length":["13"],"Content-Type":["application/json"]},"body":{"a":5,"b":3}} 2024-06-09T00:25:59.126+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-6] org.zalando.logbook.Logbook : {"origin":"remote","type":"response","correlation":"91068428bef1d88e","duration":6,"protocol":"HTTP/1.1","status":200,"headers":{"Connection":["keep-alive"],"Content-Type":["application/json"],"Date":["Sat, 08 Jun 2024 15:25:59 GMT"],"Keep-Alive":["timeout=60"],"Transfer-Encoding":["chunked"]},"body":{"result":8}} 2024-06-09T00:25:59.130+09:00 TRACE 44263 --- [api-a] [nio-8080-exec-6] org.zalando.logbook.Logbook : {"origin":"local","type":"response","correlation":"f61193f865f23243","duration":18,"protocol":"HTTP/1.1","status":200,"headers":{"Content-Type":["application/json"],"Date":["Sat, 08 Jun 2024 15:25:59 GMT"],"Transfer-Encoding":["chunked"]},"body":{"result":8}}
OKですね。
おわりに
LogbookをSpring Bootに組み込み、HTTPリクエスト・レスポンスをログ出力するようにしてみました。
今回はとりあえず導入するだけだったのですが、適用するだけなら簡単にできましたね。ここからログに記録していいもの、してはいけないもの
などをわけていったり出力条件をカスタマイズなどしていくとそれなりに大変だと思うのですが、まずは押さえるところからということで。
こういうアクセス内容を記録するものは割とよくやることだと思うので、覚えておくと便利かなと。