Next.jsのプロジェクトでSentryを7.xから8.xにアップデートする
みなさんこんにちは。株式会社スペースマーケットでWebエンジニアをしていますwado63と申します。
Next.jsのプロジェクトのdependencyをアップデートしたいなと思っていたのですが、ついでにブログにしてしまえば一石二鳥かと思い、作業しながら書き上げました。
アップデートの前提条件
弊社のNext.jsで導入しているSentryのバージョンを7.xのものからから8.xにアップデートします。
(以下バージョンは呼びやすくv7, v8とします)
弊社はSentryのインストールウィザードを使用して導入していていました。
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が用意されているのでこれを見てみましょう。
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
をつけるといいそうです。
『Apply all transformations.』を選択します。
色々と修正してくれるようですが、弊社はかなりベーシックな使い方をしていたようで、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|ts
と sentry.edge.config.js|ts
のファイルを使用しないようになりました。
代わりにNext.jsのinstrumentation hookを使用します。
最新であればstableとなっている機能ですが、弊社はまだ13系なのでexperimentalのフラグをオンにします。
module.exports = {
experimental: {
instrumentationHook: true,
},
}
これでsrc/instrumentation.tsでexportしたregister関数が実行されるとのこと
export function register() {
// Your custom code here
}
あれ?、動いている気配がない。
instrumentation hookはinstrumentation.ts
ではなく、instrumentation.page.ts
のようにnext.config.js
のpageExtensions
を考慮する必要があるのでお気をつけください😇
そういえばNext.jsのmiddlewareも同じ仕様でしたね。
instrumentation.ts
が動くことを確認できたら、既存のsentry.server.config.ts
をなくして代わりにinstrumentation.ts
でsentryを初期化させます。
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などで適用されるのでドキュメント通りに設定するといいでしょう。
ちなみにsentry.client.config.ts|js
は引き続き使用できるようです。
あれ。。。v7のSentry導入した時にsentry.edge.config.ts
は作られていなかったからmiddlewareの例外を見逃していた。。。?😨
気を取り直していきましょう。
SentryConfigの修正
next.config.js
のwithSentryConfig
の関数にいままで3つの引数を渡すことができていたのですが、これが2つになりました。
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
の第二引数に渡すようになりました。
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...
});
新しい書き方に直している際にふと問題点に気づきました。もともと設定していたdryRun
、include
のオプション指定できなくなっている?
Sentry-webpack-pluginのリポジトリを見にいくとarchiveされておりました。
新しいsentry-javascript-bundler-pluginsの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
の設定を修正します。
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のファイルしか対象にならないので、全てのファイルを対象にする
})
このように直します。
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が割り当てられるようになりました。
以前はSourcemapが意図通りにアップロードされているか調べる際Pathベースでファイルがあるか調べていたのですが、v8では該当のファイルがアップロードされているかぱっと見でわかりません😅
- 旧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){...省略
その他変更に関して
deprecatedなAPIが削除されたりしているのですが、弊社のv7からのアップデートの場合ではとくに問題になる点はありませんでした。
遭遇したissue
- jestのテストが失敗してしまう
https://github.com/getsentry/sentry-javascript/issues/12683
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に以下の設定を追加することで解決しました。
moduleNameMapper: {
'@sentry/nextjs': '<rootDir>/node_modules/@sentry/nextjs',
},
これで無事に本番環境にデプロイできたのでv8アップデート完了です。お疲れ様でした🎉
宣伝
スペースマーケットでは現在エンジニアを募集しております!
ちょっと話を聞いてみたいといったようなカジュアルな面談でも構いませんので、ご興味のある方は是非ご応募お待ちしております!
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion