🐶

Next.jsのプロジェクトでSentryを7.xから8.xにアップデートする

2024/12/26に公開

みなさんこんにちは。株式会社スペースマーケットでWebエンジニアをしていますwado63と申します。
Next.jsのプロジェクトのdependencyをアップデートしたいなと思っていたのですが、ついでにブログにしてしまえば一石二鳥かと思い、作業しながら書き上げました。

アップデートの前提条件

弊社のNext.jsで導入しているSentryのバージョンを7.xのものからから8.xにアップデートします。
(以下バージョンは呼びやすくv7, v8とします)

弊社はSentryのインストールウィザードを使用して導入していていました。
https://docs.sentry.io/platforms/javascript/guides/nextjs/

v7の時にインストールしていると以下のようなファイルがありますね。

├── .sentryclirc
├── sentry.properties
├── sentry.client.config.ts
└── sentry.server.config.ts

その他 Next.js、Sentryのバージョンとnode.jsのバージョンは以下の通りです。

"next": "^13.4.12"
"@sentry/nextjs": "^7.101.1"

"node": "20.18.1"

公式のドキュメントを読んでみる

公式にMigration Guideが用意されているのでこれを見てみましょう。
https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v7-to-v8/

The main goal of version 8 is to improve our performance monitoring APIs, integrations API, and ESM support. This version is breaking because we removed deprecated APIs, restructured npm package contents, and introduced new dependencies on OpenTelemetry.
バージョン8の主な目的は、パフォーマンスモニタリングAPI、統合API、ESMサポートを改善することです。このバージョンは、非推奨のAPIを削除し、npmパッケージの内容を再構築し、OpenTelemetryの新しい依存関係を導入したため、壊れています。

SentryもOpenTelemetry使うようになるのかーっという感想を持ちつつ、ドキュメントの上部にCodemodのコマンドが用意されていました。とても親切ですね!

npx @sentry/migr8@latest

実際に実行してみましょう。Sentryにデータ収集させたくない場合は --disableTelemetryをつけるといいそうです。
codemodの実行

『Apply all transformations.』を選択します。

codemodの実行2

色々と修正してくれるようですが、弊社はかなりベーシックな使い方をしていたようで、package.json, yarn.lockしか差分がありませんでした😀

Migration Guideを読み進めてみましょう。

サポートバージョン

Sentry v8は以下のバージョンをサポートしています。

Next.js version 13.2.0 or higher
Webpack 5.0.0 or higher
Node 14.18.0 or higher

この辺は問題なしと。

またglobalthisのためにES2018に対応しているブラウザがサポートされています。

Chrome 71
Edge 79
Safari 12.1, iOS Safari 12.2
Firefox 65
Opera 58
Samsung Internet 10

いまですとかなり古めなブラウザなのでこちらも問題なしです。

SDKの初期化方法のアップデート

v8のSentryではsentry.server.config.js|tssentry.edge.config.js|ts のファイルを使用しないようになりました。
代わりにNext.jsのinstrumentation hookを使用します。

最新であればstableとなっている機能ですが、弊社はまだ13系なのでexperimentalのフラグをオンにします。

next.config.js
module.exports = {
  experimental: {
    instrumentationHook: true,
  },
}

これでsrc/instrumentation.tsでexportしたregister関数が実行されるとのこと

src/instrumentation.ts
export function register() {
  // Your custom code here
}

あれ?、動いている気配がない。

instrumentation hookはinstrumentation.tsではなく、instrumentation.page.tsのようにnext.config.jspageExtensionsを考慮する必要があるのでお気をつけください😇
そういえばNext.jsのmiddlewareも同じ仕様でしたね。

instrumentation.tsが動くことを確認できたら、既存のsentry.server.config.tsをなくして代わりにinstrumentation.tsでsentryを初期化させます。

src/instrumentation.ts
import * as Sentry from '@sentry/nextjs';

export function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    // this is your Sentry.init call from `sentry.server.config.js|ts`
    Sentry.init({
      dsn: 'https://f4cfc66784b044be81749dc2889452fc@o87187.ingest.us.sentry.io/1271553',
      // Your Node.js Sentry configuration...
    });
  }

  // This is your Sentry.init call from `sentry.edge.config.js|ts`
  if (process.env.NEXT_RUNTIME === 'edge') {
    Sentry.init({
      dsn: 'https://f4cfc66784b044be81749dc2889452fc@o87187.ingest.us.sentry.io/1271553',
      // Your Edge Runtime Sentry configuration...
    });
  }
}

NEXT_RUNTIME === 'edge'のedge環境は、awsのLambda@EdgeやfastlyのEdge Computeを想像してしまいましたが、middlewareなどで適用されるのでドキュメント通りに設定するといいでしょう。
https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes

ちなみにsentry.client.config.ts|jsは引き続き使用できるようです。

あれ。。。v7のSentry導入した時にsentry.edge.config.tsは作られていなかったからmiddlewareの例外を見逃していた。。。?😨

気を取り直していきましょう。

SentryConfigの修正

next.config.jswithSentryConfigの関数にいままで3つの引数を渡すことができていたのですが、これが2つになりました。

next.config.js
const nextConfig = {
  // Your Next.js options...
};

-module.exports = withSentryConfig(
-  nextConfig,
-  {
-    // Your Sentry Webpack Plugin Options...
-  },
-  {
-    // Your Sentry SDK options...
-  },
-);
+module.exports = withSentryConfig(nextConfig, {
+  // Your Sentry Webpack Plugin Options...
+  // AND your Sentry SDK options...
+});

また、引数2つでnextConfigにSentry用の設定を入れていた際はwithSentryConfigの第二引数に渡すようになりました。

next.config.js
 const nextConfig = {
   // Your Next.js options...
-
-  sentry: {
-    // Your Sentry SDK options...
-  },
 };

 module.exports = withSentryConfig(nextConfig, {
   // Your Sentry Webpack Plugin Options...
+  // AND your Sentry SDK options...
 });

新しい書き方に直している際にふと問題点に気づきました。もともと設定していたdryRunincludeのオプション指定できなくなっている?
Sentry-webpack-pluginのリポジトリを見にいくとarchiveされておりました。
新しいsentry-javascript-bundler-pluginsのMIGRATION.mdを見てみます。

https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/main/MIGRATION.md

Removed dryRun option.
Removal of include for sourcemap option

消えてましたね😇

Removed the configFile option. Options should now be set explicitly or via environment variables. This also means that .sentryclirc files will no longer work as a means of configuration. Please manually pass in options, or use a configuration file (Webpack plugin docs, Vite plugin docs, esbuild plugin docs, Rollup plugin docs).

.sentryclircも使えなくなっているようです。

他にもdefault.projectなどが書かれていたsentry.propertiesも読み込まれなくなっているようです。これについて言及しているところが見つからなかったのですが、こちらのPRで消えておりました。
このあたりはbuild時に環境変数渡したり、withSentryConfigの引数に渡すようにすればいいでしょう。

ということで以下のnext.config.jsの設定を修正します。

next.config.js
const { withSentryConfig } = require('@sentry/nextjs')

const nextConfig = {
  ...baseConfig, // 他の設定など
  sentry: {
    hideSourceMaps: true,
  },
}

module.exports = withSentryConfig(nextConfig, {
  dryRun: (process.env.NEXT_PUBLIC_ORIGIN ?? '').includes('localhost'), // 以前はlocalhostの時だけsentryを無効にしていた
  include: '.next', // デフォルトだとpagesのファイルしか対象にならないので、全てのファイルを対象にする
})

このように直します。

next.config.js
const { withSentryConfig } = require('@sentry/nextjs')

module.exports = withSentryConfig(nextConfig, {
  widenClientFileUpload: true,
  sourcemaps: {
    deleteSourcemapsAfterUpload: true,
  },
})

dryRunに関して動かしたくない時はSENTRY_AUTH_TOKENの環境変数を指定しなければいいだけですし、Soucemapの検証などしたい場合は適当な開発用のprojectを用意してあげればいいので、とくに困りませんでした。

includeに関しては、以前static/chunksのファイルをアップロードするために指定していたのですが、widenClientFileUploadというオプションがあったのでこれを使うようにしました。
(Sentry v7でも用意されていたのですが、気づいていなかった。。。)
ちなみにwidenClientFileUploadを指定していてもframework-,main-などのファイルはアップロードされませんがSentry側でそのように作っているのでそのまま受け入れるようにします。
開発者が作ったエラーに集中して欲しいという意図があるようです。

ちなみに、全部アップロードしたいんだ!という場合は、sourcemaps.ignore[]を指定するとアップロードできました。

そして、sentry.hideSourceMapsの代わりにsourcemaps.deleteSourcemapsAfterUploadのオプションを指定することで、アップロード後にソースマップを削除してくれるようです。嬉しい!
以前のhideSourceMapsを指定している際は、そのままだとSourcemapファイルがホスティングされてしまうので自前でpost_buildのスクリプトを用意して削除していましたが不要になりました。

SourcemapのDebugIdについて

v8からはSourcemapにDebug IDが割り当てられるようになりました。
https://docs.sentry.io/platforms/javascript/guides/nextjs/sourcemaps/troubleshooting_js/artifact-bundles/

以前はSourcemapが意図通りにアップロードされているか調べる際Pathベースでファイルがあるか調べていたのですが、v8では該当のファイルがアップロードされているかぱっと見でわかりません😅

  • 旧Sourcemapの例
    旧Sourcemap
  • 新Sourcemapの例
    新Sourcemap

出力されているファイルをみると_sentryDebugIdsが追加されているので、これを元にSourcemapを特定するようになっています。

!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fee14266-9722-45d4-b20a-1beffe2674c4",e._sentryDebugIdIdentifier="sentry-dbid-fee14266-9722-45d4-b20a-1beffe2674c4")}catch(e){...省略

IDでSourcemapを検索

その他変更に関して

deprecatedなAPIが削除されたりしているのですが、弊社のv7からのアップデートの場合ではとくに問題になる点はありませんでした。

遭遇したissue

jest.mock('@sentry/nextjs')としても正しくmockができないようです。
jestを実行時に弊社の場合以下のようなエラーが出てました。

stack traceを見ていくと、mockしているはずの@sentry/nextjsでエラーが出ていることに気づきます。

  ● Test suite failed to run

    TypeError: Cannot read properties of undefined (reading 'events')


    at Object.<anonymous> (node_modules/@sentry/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts:19:50)
      at Object.<anonymous> (node_modules/@sentry/nextjs/build/cjs/client/routing/nextRoutingInstrumentation.js:5:43)
      at Object.<anonymous> (node_modules/@sentry/nextjs/build/cjs/client/browserTracingIntegration.js:4:36)
      at Object.<anonymous> (node_modules/@sentry/nextjs/build/cjs/client/index.js:12:35)
      at Object.<anonymous> (node_modules/@sentry/nextjs/build/cjs/index.client.js:3:15)
      at Object.<anonymous> (src/components/pages/reservationRequest/ReservationRequestFormPage/components/ConfirmView/__dev__/index.test.tsx

ちょっとjest.configの設定が違うと、moduleが見つからないというエラーが出ることもあるようです。

  ● Test suite failed to run

    Cannot find module '@sentry/nextjs' from ...省略

弊社の場合はこちらのコメントを参考にjest.config.jsに以下の設定を追加することで解決しました。

jest.config.js
 moduleNameMapper: {
    '@sentry/nextjs': '<rootDir>/node_modules/@sentry/nextjs',
  },

これで無事に本番環境にデプロイできたのでv8アップデート完了です。お疲れ様でした🎉

宣伝

スペースマーケットでは現在エンジニアを募集しております!
ちょっと話を聞いてみたいといったようなカジュアルな面談でも構いませんので、ご興味のある方は是非ご応募お待ちしております!

スペースマーケット Engineer Blog

Discussion