Sentry Legacy SDK(1.7系)を6.x系まで一気にバージョンアップした話

## はじめに
- この記事は　ZOZO Advent Calendar 2023 #1 23日目の記事になります

## 何をしたか
- Java Sentry Legacy SDK(1.7系) - spring を6.x系まで一気にバージョンアップしました
- 使い方が色々変わっていたが、migration guideがあまり参考にならなかったので対応内容を備忘録としてまとめます

## なぜしたか
- 2023/11/24のSpring Boot 2.7.x End of Supportに伴い、ZOZOで稼働中のマイクロサービスの一部のSpring Bootのバージョンを3.1.x系に上げました
  - 前提条件としてJava11->17の移行も同時に行ったがそこは本記事では割愛します
- ZOZOではエラー検知の仕組みの一つしてSnetryを運用しています。今回Spring BootバージョンアップしたマイクロサービスではSentry Legacy SDK(1.7系)を利用していましたが、Spring Boot 3系のバージョンアップ時にjavax->jakartaのパッケージ変更が必要となった関係で使えなくなりました
  - jakartaに対応したsentry-springは

## やったこと1
- なにはともあれdependenciesの変更
  ```diff:build.gradle
  - implementation 'io.sentry:sentry-spring:1.7.5'
  + implementation 'io.sentry:sentry-spring-boot-starter-jakarta:6.32.0'
  ```
- jakarta対応のライブラリは`sentry-spring-jakarta`もありましたが、最低versionが`6.7.x`だったのでこの際と思い`sentry-spring-boot-starter-jakarta`を使うことにしました

## やったこと2
- `SentryExceptionResolver`が使えなくなったので`CustomEventProcessor`を追加
  - sentry-spring 1.7系では、SentryExceptionResolverをBean登録することで、Controllerでthrowされる例外がすべてSentryに送信されます
    - また、resolveExceptionメソッドをOverrideすることで、requestの情報などを取得でき、Sentryへの通知メッセージを改変することができます
  ```java
  public class CustomSentryExceptionResolver extends SentryExceptionResolver {
      @Override
      public ModelAndView resolveException(
          HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
          EventBuilder eventBuilder =
              (new EventBuilder())
                  .withMessage("sample message")
                  .withLevel(Event.Level.ERROR)
                  .withSentryInterface(new ExceptionInterface(ex));
          Sentry.capture(eventBuilder);
      }
  }
  ```

  - 6.x系では`SentryExceptionResolver`は使えないので、同様のことを実現する方法として`EventProcessor`を使いました
  ```java
  @Component
  public class CustomEventProcessor implements EventProcessor {
      @Override
      public SentryEvent process(SentryEvent event, Hint hint) {
         Message sentryMessage = new Message();
          sentryMessage.setMessage("sample message");
          event.setMessage(sentryMessage);
          event.setLevel(SentryLevel.ERROR);
          return event;
      }
  }
  ```
  - `HttpServletRequest`は以下のコードで取得可能です
  ```java
  HttpServletRequest request = hint.getAs(SPRING_RESOLVER_REQUEST, HttpServletRequest.class);
  ```

## やったこと3
- やったこと2とも関連しますが、event関連のpackageとSentryへのマニュアルメッセージ通知メソッドが変更となっていますので、ドキュメントを参照し修正しました
```diff
- import io.sentry.event.Event;
- import io.sentry.event.EventBuilder;
- import io.sentry.event.interfaces.ExceptionInterface;
+ import io.sentry.SentryEvent;
+ import io.sentry.SentryLevel;
+ import io.sentry.protocol.Message;

@Component
public class SentryHelper {
  public void capture(String logMessage, Exception e) {
-     EventBuilder eventBuilder =
-         new EventBuilder()
-             .withMessage(logMessage)
-             .withLevel(Event.Level.ERROR)
-             .withSentryInterface(new ExceptionInterface(e));
-     Sentry.capture(eventBuilder);
-   }
+     SentryEvent sentryEvent = new SentryEvent();
+     Message sentryMessage = new Message();
+     sentryMessage.setMessage(logMessage);
+     sentryEvent.setMessage(sentryMessage);
+     sentryEvent.setLevel(SentryLevel.ERROR);
+     sentryEvent.setThrowable(e);
+     Sentry.captureEvent(sentryEvent);
  }   
```
## やったこと4
- 6.x系ではデフォルトでは`unhandled exception`のみがSnetryに通知されます。以下の設定をすることで`handled exception`も通知されます
  ```yaml:application.yml
  sentry:
      exception-resolver-order: -2147483647
  ```

## まとめ
- `sentry:sentry-spring-boot-starter`に移行したことで、使い方はよりシンプルになり実装しやすくなりました
- sentry-springのバージョンが大幅に上がったことでSentryに通知される情報もより詳細になり、エラー要因の調査もしやすくなりました
  - これからsentry-spring Legacy SDKをバージョンアップしようとしている方の参考になれば幸いです

## 参考
- https://docs.sentry.io/platforms/java/guides/spring-boot/legacy/spring/
- https://docs.sentry.io/platforms/java/