Springでリクエストとレスポンスのログを出力する
Spring MVCでリクエストとレスポンスのログを出力する方法を説明します。
リクエストログだけなら CommonsRequestLoggingFilter
もしくは AbstractRequestLoggingFilter
を使う方法が簡単です。詳しくは下記の記事で説明しています。
一方で、レスポンスログを出力するには一工夫が必要です。通常、レスポンスボディはストリームに書き込まれてメモリから消えてしまうため、メモリに一時的に保持する必要があります。Springで用意されている ContentCachingResponseWrapper
というクラスを使うと、レスポンスボディをバイト配列で取り出すことができます。
実装例
AbstractRequestLoggingFilter
を参考にしてフィルタを実装してみました。
ContentCachingRequestWrapper
でリクエストオブジェクトをラップする。ContentCachingResponseWrapper
でレスポンスオブジェクトをラップする。- 前処理で、リクエストオブジェクトからヘッダとボディを取り出してログに出力する。
- 後処理で、レスポンスオブジェクトからヘッダとボディを取り出してログに出力する。
なお、後処理では必ず ContentCachingResponseWrapper#copyBodyToResponse()
を実行する必要があります。そうしないとレスポンスボディがストリームに書き込まれないため、Tomcatの場合は待ち状態になってしまいます。Jettyでは insufficient content written
というエラーが返されます。
このフィルタを適用するとレスポンスボディが一時的にメモリに保持されるため、メモリの消費量が大きくなります。また、大きいレスポンスを返す場合はパフォーマンスが悪化します。そのため、デバッグ用途に限定した方がよいでしょう。
#渋谷java でSwaggerのテンプレートを魔改造した話をした
第二十回 #渋谷java で、複数チームの並行開発におけるSwagger(OpenAPI)の活用についてお話しさせていただきました。
TL;DR
- 複数チームが並行開発を行う場合はインクリメンタルなAPI設計は避けられない。
- OpenAPI YAMLからAPI Server, Clientを生成し、API Clientを利用側に共有することで、APIを安全にリファクタリングできる。
- 自動生成されたコードを一切修正できない仕組みを強制することで、チーム開発の混乱を防ぐ。
実際にSwagger Codegenのテンプレートをカスタマイズした内容もお話ししました。
久しぶりにマニアックな話を聞けて楽しかったです。運営のみなさま、ありがとうございました!
Spring Bootアプリケーションのログファイル運用
Spring Bootアプリケーションのログファイル運用についてメモ。
前提
Spring Bootのログ
Spring Bootのログはデフォルトでは標準出力に出力される。ログファイルを出力するにはapplication.ymlで設定するか、JVMに起動オプションを渡す。
java -jar app.jar --logging.path="$LOG_PATH"
上記を指定すると、10MBのサイズでローテーションされる。最大で8世代まで保持される。
spring.log
←最も新しいspring.log.1
(10MB)spring.log.2
(10MB)- …
spring.log.7
(10MB) ←最も古い
最大世代に達した場合は最も大きい番号のログファイルが削除されて、番号がシフトされる。上記の場合は .7
が削除されて、.6
が .7
にリネームされる。
FluentdやCloudWatch Logsでログを収集する場合は spring.log
をtailすればよい。8世代を超えたものは自動的に削除されるので、ガベージは気にしなくてよい。
GCのログ
GC(ガベージコレクション)のログはデフォルトでは出力されない。ログファイルを出力するにはJVMに起動オプションを渡す必要がある。
JAVA_OPTS=( "-Xloggc:$LOG_PATH/gc.log" "-XX:+UseGCLogFileRotation" "-XX:GCLogFileSize=10M" "-XX:NumberOfGCLogFiles=5" "-XX:+PrintGCDetails" "-XX:+PrintGCDateStamps" ) java "${JAVA_OPTS[@]}" -jar app.jar
上記を指定すると、10MBのサイズでローテーションされる。最大で5世代まで保持される。
gc.log.0
←古いものgc.log.1.current
←現在アクティブなもの
現在アクティブなものに .current
が付与される。最大世代に達した場合は小さい番号から再利用される。
FluentdやCloudWatch Logsでログを収集する場合は gc.log.*.current
をtailすればよい。8世代を超えたものは自動的に削除されるので、ガベージは気にしなくてよい。