Misskey & Webテクノロジー最前線

Sentryで始めるエラートラッキング

本連載では分散型マイクロブログ用ソフトウェアMisskeyの開発に関する紹介と、関連するWeb技術について解説を行っています。

今回はMisskeyで使用可能なサードパーティのエラートラッキングサービス、Sentryとその活用について紹介します。

エラートラッキングとは

Misskey 2024.5.0から、外部のエラートラッキングサービスとしてSentryをサポートするようになりました。

エラートラッキングサービスは、ソフトウェア内で発生したエラーを一元化して収集・管理できるサービスです。Misskey Projectでは開発用のステージング環境を用意しており、そのサーバー内で発生したエラーを捕捉し、開発チーム内で共有する必要があったため導入しました。

エラートラッキングサービスはいくつか存在しますが、SentryはWebブラウザ・Node.jsのほか、様々なプラットフォームで動作します。

ノート

Sentryの一部機能は有料プランが必要となっていますが、OSSのための利用であれば申請を行うことで当該機能を無料で使用することが可能です。

導入

Sentryでは各プラットフォーム用のSDKが提供されていますのでそれを使用します。今回はバックエンド側の実装を紹介します。

まずSDKをインストールします。

npm install --save @sentry/node @sentry/profiling-node

次に、Sentry SDKの初期化を行うためのファイルsentry.tsを作ります。内容は以下の通りです。

import * as Sentry from '@sentry/node';
import { nodeProfilingIntegration } from '@sentry/profiling-node';

Sentry.init({
  dsn: 'https://[email protected]/0',
  integrations: [
    nodeProfilingIntegration(),
  ],
  tracesSampleRate: 1.0,
  profilesSampleRate: 1.0,
});

なお、dsnはSentryの個々のプロジェクトごとに発行されるものを入れます。

最後に、アプリケーションのエントリーファイルで上記のファイルをimportします。

import './sentry.js';

これでSentryの導入はできました。

ノート

nodeProfilingIntegrationは後述するパフォーマンス計測のためのプラグインなので、純粋にエラートラッキングするだけであれば不要です。

エラーのトラック

実際にエラー(Sentry上では「Issue」と表現されます)をトラックするには、Sentry.captureExceptionメソッドを利用して例外オブジェクトを直接渡したり、Sentry.captureMessageメソッドを利用してカスタムメッセージを渡します。

Misskeyのバックエンドでは、API呼び出し時にハンドルされない例外が発生するとSentryに報告する実装になっています。

当該箇所の実装は以下となっています。

Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, {
	level: 'error',
	extra: {
		ep: ep.name,
		ps: data,
		e: {
			message: err.message,
			code: err.name,
			stack: err.stack,
			id: errId,
		},
	},
});

captureMessageでは、extraオプション内に追加の情報を含めることができます。

このようにSentryに報告されたエラーは、Sentryのダッシュボードにて詳細を確認できます。

図

Resolve, Archive, Assign

報告されたエラーの対応には大まかに3パターンあり、修正(Resolve)・アーカイブ(Archive)・担当者割り当て(Assign)があります。

Resolveは、当該エラーを修正したときに行います。もしResolveした後に同様のエラーが発生すると、リグレッションとして再通知されます。

Archiveは、当該エラーを無視したいときに行います。例えば、一時的なエラーで修正の必要がなかったり、修正が難しいようなエラーを通知したくないときにアーカイブします。

エラーサンプリング

大規模なアプリケーションでは発生するエラーの数も多くなる場合があります。それらすべてをSentryに送信していると負荷も高まりますし料金も高くなってしまいます。

そこでSentryでは発生したエラーをサンプリングして報告するオプションがあり、SDK初期化時にレートをsampleRateで指定できます。デフォルトは1で、これは発生したエラーをすべて報告する設定です。50%の確率で報告するようにしたい場合は0.5にします。

パフォーマンス計測

Sentryにはエラーのトラッキング以外にもパフォーマンス計測機能が用意されています。

Misskeyのバックエンドでは、API呼び出しごとにパフォーマンス計測を行うようにし、どのAPIの負荷が高いのか調べられるようにしています。

パフォーマンス計測を行うにはSentry.startSpanメソッドを利用します。

Sentry.startSpan({ name: 'some expensive functions' }, async () => {
	await foo();
	await bar();
});

startSpanのコールバック内で実行される処理はパフォーマンス計測の対象になり、ダッシュボードで確認できるようになります。

図2

クエリ監視

データベースに対するクエリのパフォーマンスを計測する機能もあり、N+1クエリの発見にも役立ちます。

N+1クエリとは、典型的には列挙された値一つ一つに対してクエリを発行してしまうことです。例えばユーザーIDが10個含まれるリストがあったとして、そのリストに含まれるユーザー情報をデータベースから取得する際に10回クエリを発行してしまうようなケースです。この場合、本来はINなどのSQLを使うことでクエリは1回で済むので無駄な処理になってしまっています。

Sentryではそのようなクエリがないか監視できます。

実際にMisskeyで報告されたN+1クエリ
図3

なお、対応しているデータベースドライバを使用している場合、この機能を使うのに特に設定は要りません。

サードパーティインテグレーション

Sentryでは、様々なプラットフォームと連携して機能を拡張するオプションが用意されています(利用できるのは特定プランのみ⁠⁠。

Misskeyの開発チームではDiscordをコミュニケーションツールとして利用していますが、SentryのDiscord連携機能を活用することで、ステージング環境内でエラーが発生した際にリアルタイムでDiscordに通知するような仕組みを構築しています。

図4

Discord連携に関するドキュメントは以下にあります。

もちろんSlackなど他プラットフォームにも対応しています。

まとめ

Misskeyの開発用ステージング環境でSentryとDiscord連携を導入することによって、今まで気づいていなかったようなエラーの炙り出しを行うことができ、デバッグの効率化が行えました。

ソフトウェアの正式リリース前はこのようなエラーを早期発見することが品質を保証する上で重要です。

今のところバックエンドのみの導入ですが、今後フロントエンドにも導入予定なので、その際はVueとの連携について等、また解説を行いたいと思います。

おすすめ記事

記事・ニュース一覧