GeekFactory

int128.hatenablog.com

Springでリクエストとレスポンスのログを出力する

Spring MVCでリクエストとレスポンスのログを出力する方法を説明します。

リクエストログだけなら CommonsRequestLoggingFilter もしくは AbstractRequestLoggingFilter を使う方法が簡単です。詳しくは下記の記事で説明しています。

int128.hatenablog.com

一方で、レスポンスログを出力するには一工夫が必要です。通常、レスポンスボディはストリームに書き込まれてメモリから消えてしまうため、メモリに一時的に保持する必要があります。Springで用意されている ContentCachingResponseWrapper というクラスを使うと、レスポンスボディをバイト配列で取り出すことができます。

実装例

AbstractRequestLoggingFilter を参考にしてフィルタを実装してみました。

  • ContentCachingRequestWrapper でリクエストオブジェクトをラップする。
  • ContentCachingResponseWrapper でレスポンスオブジェクトをラップする。
  • 前処理で、リクエストオブジェクトからヘッダとボディを取り出してログに出力する。
  • 後処理で、レスポンスオブジェクトからヘッダとボディを取り出してログに出力する。

なお、後処理では必ず ContentCachingResponseWrapper#copyBodyToResponse() を実行する必要があります。そうしないとレスポンスボディがストリームに書き込まれないため、Tomcatの場合は待ち状態になってしまいます。Jettyでは insufficient content written というエラーが返されます。

Java 8 + Lombokで書くとこんな感じです。

このフィルタを適用するとレスポンスボディが一時的にメモリに保持されるため、メモリの消費量が大きくなります。また、大きいレスポンスを返す場合はパフォーマンスが悪化します。そのため、デバッグ用途に限定した方がよいでしょう。

#渋谷java でSwaggerのテンプレートを魔改造した話をした

第二十回 #渋谷java で、複数チームの並行開発におけるSwagger(OpenAPI)の活用についてお話しさせていただきました。

speakerdeck.com

TL;DR

  • 複数チームが並行開発を行う場合はインクリメンタルなAPI設計は避けられない。
  • OpenAPI YAMLからAPI Server, Clientを生成し、API Clientを利用側に共有することで、APIを安全にリファクタリングできる。
  • 自動生成されたコードを一切修正できない仕組みを強制することで、チーム開発の混乱を防ぐ。

実際にSwagger Codegenのテンプレートをカスタマイズした内容もお話ししました。

久しぶりにマニアックな話を聞けて楽しかったです。運営のみなさま、ありがとうございました!

togetter.com

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世代を超えたものは自動的に削除されるので、ガベージは気にしなくてよい。