Sentry の Web Vitals を Slack に統合する
はじめに
Web Vitals は Google が提唱した取り組みで、ウェブサイトのパフォーマンスを定量化するための様々なメトリクスを提供することを主な目的としている。以前 Google Chrome Developer チャンネルで台湾語を使って Core Web Vitals を紹介する動画があったが、例えがとても面白くておすすめだ ↓
現在、多くのウェブサイトで Web Vitals の計測プロセスが導入されており、定量的な指標を通じて改善点を見つけられるようになっている。開発者が Web Vitals を計測するのに役立つツールは現在かなり多く存在する。例えば:
- Lighthouse
- PageSpeed Insights
- CrUX
- Search Console
Sentry の Web Vitals 統計機能
だが、あまり知られていないかもしれないが、実は Sentry も以前から Web Vitals の統計機能1をリリースしている。LCP、FP、CLS といった Web Vitals の各指標を集計でき、開発者は管理画面で関連するグラフを確認できるうえ、平均値までプロットしてくれていてかなり親切だ。

しかし、Sentry のデータは 30 日間しか保持されないようで、先週のメトリクスと今週のメトリクスを比較するといった対比機能もない。グラフも Sentry 上でしか見られないため、他のチームにとってはコンテキストスイッチのコストになる。開発者としては、やはり一連のプロセスを自動化したくなる。
そこで調べてみたところ、Sentry は(トークンがあれば)API 連携 を提供しているものの、Web Vitals に関する API ドキュメントは見当たらなかった。だが気になってコンソール(DevTools)を開いてみたところ、グラフの生成も API 経由で取得されていることが分かった。僕が API のエンドポイントとパラメータをそのままコピーして cURL で叩いてみたところ、見事に成功した! どうやら単にドキュメントに記載されていないだけのようだ。

主な API は次の 2 つだ:(ドキュメントがないため、パラメータやパスが変更される可能性もある)
/events-measurements-histogram/?.../eventsv2
自動化プロセスの設計
データが手に入ったということは、Slack に統合して定期的に追跡できるし、他のチームメンバーへの共有もしやすくなる。さらに詳細なデータが見たい場合は Sentry の管理画面を見に行けばいい。フロー全体は以下の通りだ:

cron ジョブ経由で API を呼び出し(あるいはサーバーレスで実装してもよい)、Sentry のデータを取得したあとにアップロードしてレポートを生成する。
ひとつ特に注意が必要な点として、クエリパラメータの中に + が含まれている。そのまま encodeURIComponent に渡したり URLSearchParams を使ったりすると %2B に変換されてしまい、Sentry 側でエラーが返ってくる。そのため、+ を個別に取り出すか、エンコードされないように工夫する必要がある。
例えば、API のクエリパラメータは field=percentile(measurements.fp%2C+0.75)&field=percentile(measurements.fcp%2C+0.75)&... のような形になる。関数を呼び出すような書き方で返してほしいデータを定義でき、例えば percentile(measurements.fp,+0.75) なら 75 パーセンタイルの FP データ(単位はおそらくミリ秒)が返される。よく見ると、, は %2C にエンコードされているが、+ の部分はエンコードされていないことがわかる。クエリパラメータを組み立てる際にエラーが出る場合は、ここを確認してみるといいだろう。
返却されるデータの形式は以下のようになる:
{
"path": "/my-page",
"data": [
{
"count": 10,
"bin": 2000
},
...
]
}
あとは .json を生成して S3(または他のストレージ)にアップロードするだけだ。こうすることで、昨日(あるいはそれ以前)のデータと比較し、レポートを生成して Slack に送信できるようになる:

なお、グラフを生成したい場合は、Node.js で chart.js と chartjs-node-canvas を組み合わせて実装できる。これならブラウザ(Canvas)を使わずにグラフを描画可能だ。唯一注意すべきなのは、サーバー側に対応するフォントがインストールされていないと、レイアウトが崩れたり文字化け・フォントのズレが発生したりする可能性がある点だ。

cron ジョブの選定については、現在では成熟したソリューションが数多くあり、サーバーを立てて直接 crontab に書き込んでも構わないが、今回は Drone CI の cron ジョブ機能を使って実装した。Drone の設定ファイルは記述がとても手軽であり、社内に専用の Drone CI サーバーがあること、また外部サービスを安易に使えない事情もあったため、Drone CI に統合するのが最も手っ取り早く、セットアップも容易だったからだ。
ただ、なぜか cron イベントがトリガーされるたびに他のパイプラインまで一緒にトリガーされてしまったため、最終的には別途ブランチを切り、そのブランチの .yaml を編集して API を呼び出す方式に変更した:
steps:
- name: upload and report
image: byrnedo/alpine-curl
commands:
- curl -X POST YOUR_SERVER_ENDPOINT
これで自動化の実装は完了だ。今では毎日レポートが Slack に届くようになり、開発者にとっても変化やフィードバックをより簡単に把握できるようになった。
Footnotes
関連記事
- Three.js で僕の部屋を表現する 僕が React Three Fiber を使って実際の自分の部屋をブラウザ上に再現し、実世界のオブジェクトを目録に見立て、空間の記憶を通してここ数年の生活と仕事について語った話。
- フロントエンドで画像を扱う際に注意すべきこと Jake Archibaldの記事を起点に、現代のレスポンシブ画像の書き方を整理する。なぜwidth/heightを付ける必要があるのか、CSSのaspect-ratioはいつ使うべきか、AVIFとWebPの選び方、そしてpicture/source/srcsetを使ったモバイル向け画像の切り替えについて。
- CSS field-sizing — たった1行のCSSでフォーム要素を自動リサイズする かつてtextareaの自動高さ調整は、JavaScriptでscrollHeightを監視するしかなかった。しかしCSSのfield-sizing: contentなら、わずか1行で代替でき、textarea、input、selectに対応している。本記事では従来のやり方のペインポイントと、field-sizingの使い方をまとめる。
- リンクの下線をもっと見栄え良くする:text-underline-offset デフォルトでは下線と文字が近すぎて、このスタイルを好まないデザイナーもいるし、僕自身もあまり綺麗ではないと感じていた。