【NestJS】Sentryの使い方
Sentryのライブラリ
選択肢は3つある。
-
@sentry/node
- Sentry公式のNode用ライブラリ
-
@sentry/nestjs
- Sentry公式のライブラリ
- 2024/07/10にリリースされたばかりで、2024/07/30現在だとまだアルファ版
-
@ntegral/nestjs-sentry
- サードパーティ製のライブラリ
おそらく今後は@sentry/nestjs
が主流になっていくと思うが、2024/07/30現在だとまだアルファ版なので@sentry/node
を使うことにする。
@sentry/nodeをNestJSにインストール
インストール
npm install @sentry/node
sentry用の設定ファイル
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
// ...
});
グローバルフィルターを作成
import { ArgumentsHost, Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import * as Sentry from '@sentry/node';
@Catch()
export class AllExceptionFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// Sentryにエラー通知する
Sentry.captureException(exception);
super.catch(exception, host);
}
}
グローバルフィルターを適用
// sentryは一番最初にimportする
import './sentry/instrument.js';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { AppModule } from './app.module.js';
import { AllExceptionFilter } from './filters/all-exception.filter.js';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionFilter(httpAdapter));
await app.listen(3000);
}
bootstrap();
Sentryにエラー通知されることを確認
以下のようにエラーを発生させるとSentryに通知される。
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service.js';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
// 意図的にエラーを発生させる
throw new Error('sample error');
return this.appService.getHello();
}
}
4xxエラーのときはSentry通知したくない
このままだと存在しないパスにアクセスがあったときの404エラーなどもSentry通知されてしまうので、5xxエラーのときだけSentry通知するように設定する。
import { ArgumentsHost, Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import * as Sentry from '@sentry/node';
@Catch()
export class AllExceptionFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// HttpExceptionが発生した場合、statusCodeが設定される
const statusCode = (exception as { status?: number }).status;
// HttpException以外の例外、もしくは500系エラーの場合はSentryに通知する
if (statusCode === undefined || statusCode >= 500) {
Sentry.captureException(exception);
}
super.catch(exception, host);
}
}
ちなみに@sentry/nestjs
を使う場合はデフォルトで4xxエラーは通知されないように設定されてある。
Sentryに通知されるタイトルとメッセージを変える
それぞれ、errorのnameとmessageが表示されている。
CustomErrorを発生させた場合
class CustomError extends Error {}
throw new CustomError('sample error');
クラスを変えただけではSentry側のタイトルは変わらない。
nameを変更したCustomErrorを発生させた場合
class CustomError extends Error {
constructor(message: string) {
super(message);
this.name = 'CustomError';
}
}
throw new CustomError('sample error');
タイトルが変わる。
messageを変えたとき
throw new CustomError('errrrrrrrrrrr');
メッセージが変わる。
どのようにエラーがグルーピングされるのか?
似たようなエラーはグルーピングされるが、ほぼ同じ内容なのに違うエラーとして認識されることもある。
グルーピングはスタックトレースやエラーのタイプ、メッセージによって行われる。
参考:https://docs.sentry.io/product/issues/grouping-and-fingerprints/
What is a fingerprint?
A fingerprint is a way to uniquely identify an event. Sentry sets a fingerprint for you by default, using built-in grouping algorithms based on information such as a stack trace, exception type, and message. Events with the same fingerprint are grouped together.フィンガープリントとは何か?
フィンガープリントはイベントを一意に識別する方法です。 Sentry はデフォルトでフィンガープリントを設定し、スタックトレース、例外タイプ、メッセージなどの情報に基づいた組み込みのグルーピングアルゴリズムを使用します。 同じフィンガープリントを持つイベントは一緒にグループ化されます。
つまり発生箇所(スタックトレース)が変わればエラーメッセージが同じでも別のエラーとして認識される。
エラー発生時のリクエストボディやヘッダーについて
どこで設定してくれているのか詳しく分かっていないが、自動でSentry側でデータが取れている。
Contextの付与
Sentry.setContext('character', {
name: 'Mighty Fighter',
age: 19,
attack_type: 'melee',
});
Sentry.captureException(exception);
Additional Dataの付与
Additional Dataは非推奨で、Contextを使ってほしいとのこと。
以下のように付与することは可能。
Sentry.setExtras({
'my-additional-info': 'this is additional info',
'my-additional-info2': 'this is additional info2',
});
Sentry.captureException(exception);
タグの付与
Sentry.setTags({
'my-tag': 'my-tag-value',
'my-tag2': 'my-tag-value2',
});
Sentry.captureException(exception);
ユーザー情報の付与
Sentry.setUser({
id: 123,
username: 'sample-username',
email: 'test@example.com',
});
Sentry.captureException(exception);
ユーザー数もカウントされる。