prismatix事業部の太田です。
Spring Boot + Kotlinで構成されたアプリケーションにSentryを導入する検証を行ったため、メモがてら知見を残します。
バージョンなど
- Spring Boot 3.1.0
- Kotlin 1.8.21
- JDK OpenJDK Runtime Environment Corretto-17.0.7.7.1 (build 17.0.7+7-LTS)
導入
専用のSDKが用意されているので導入します。
また、logging統合のライブラリを追加すると、ログ出力した内容もSentryに送信する設定を自動で行ってくれます。
build.gradle
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
// Sentry SDK 2系の場合は別のライブラリなので注意
implementation("io.sentry:sentry-spring-boot-starter-jakarta:6.19.1")
// logback統合
implementation("io.sentry:sentry-logback:6.19.1")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
Sentryの管理画面から取得したdsnを設定します。
build.gradle
sentry:
dsn: ${SENTRY_DSN}
これで送信する準備が整いました。
ExceptionHandlerで捕捉される例外についてもSentryに送信する
上記の設定では、unhandled exception
、及びlogbackで出力されたerrorログの内容がSentryに送信されます。
一方で、Spring BootでWebアプリケーションを実装している場合、@ControllerAdvice
と@ExceptionHandler
を用いた例外ハンドリングを行っていることが多いのではないでしょうか。
以下の設定を追加することで、@ExceptionHandler
によって捕捉される例外についてもSentryに送信することができます。
build.gradle
sentry:
dsn: ${SENTRY_DSN}
sentry.exception-resolver-order=-2147483647 // the value of org.springframework.core.Ordered#HIGHEST_PRECEDENCE
Sentryが実装しているHandlerExceptionResolver
の優先度を高めることで、ハンドリング順をコントロールしているようです。
実行サンプル
以下のように、@ExceptionHandler
で例外をエラーログ出力に変換した場合と、そのままSpringの領域にthrowした場合のコードで実験してみました。
SampleController.kt
package com.example.demo
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.GetMapping
@Controller
class SampleController {
@GetMapping("/sample/handled")
fun sampleHandled() {
throw HandledRuntimeException("handled exception sample")
}
@GetMapping("/sample/unhandled")
fun sampleUnhandled() {
throw UnhandledRuntimeException("unhandled exception sample")
}
}
@ControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(HandledRuntimeException::class)
fun handleRuntimeException(e: HandledRuntimeException) {
LoggerFactory.getLogger(this::class.java).error("exception handler sample")
}
}
class HandledRuntimeException(message: String) : RuntimeException(message)
class UnhandledRuntimeException(message: String) : RuntimeException(message)
結果
以下のように、エラーログについてはハンドリングされる順番に関わらずSentryへ送信されましたが、例外についてはハンドリングされる順番によって結果が変化しました。
sentry.exception-resolver-order | error log | handled exception | unhandled exception |
---|---|---|---|
最優先(-2147483647) | 送信される | 送信される | 送信される |
設定なし | 送信される | 送信されない |
送信される |
最後に
この記事ではSpring Bootのよくある実装パターンである@ExceptionHandler
とSentryの関係性について見てきました。
エラー監視は便利ですが、大量に出力されると運用が辛いため、想定外のエラーだけ検知されるようにうまく量をコントロールできる設定にできると良いですね。