Closed4
ApolloServerを使ったGraphQLアプリケーションにエラー検知ツールを導入
背景
サーバー側のGraphQLアプリケーションにエラー検知ツールを導入したい
ApolloServer + node.js + TypeScript という構成
なるべく無料で済ませたい
Sentry
無料枠の懸念点
- SlackのIntegrationがない
- webhookのintegrationを試したが以下のようなエラーが出て動かず
- https://webhook.site/ に送ってみた感じwebhook自体は動いているがslackが受け付ける形式ではなかったのか
- そもそもwebhookのitengrationはlegacyっぽいので率先して使いたくないので調査終了
- メールの通知はある
- メールだと色々ごちゃごちゃするのでSlackが良い
- 特定の件名だったらSlackに通知みたいな仕組み作れそうだが面倒なのでやってない
導入方法
https://blog.sentry.io/2020/07/22/handling-graphql-errors-using-sentry に準拠
npm install @sentry/node @sentry/tracing
import * as Sentry from '@sentry/node';
import {ApolloError} from 'apollo-server';
import {ApolloServerPlugin} from 'apollo-server-plugin-base';
Sentry.init({
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
});
export const sentryPlugin = (): ApolloServerPlugin => {
return {
async requestDidStart(_) {
/* Within this returned object, define functions that respond
to request-specific lifecycle events. */
return {
async didEncounterErrors(ctx) {
// If we couldn't parse the operation, don't
// do anything here
if (!ctx.operation) {
return;
}
for (const err of ctx.errors) {
// Only report internal server errors,
// all errors extending ApolloError should be user-facing
if (err instanceof ApolloError) {
continue;
}
// Add scoped report details and send to Sentry
Sentry.withScope((scope) => {
// Annotate whether failing operation was query/mutation/subscription
scope.setTag('kind', ctx.operation?.operation);
// Log query and variables as extras
// (make sure to strip out sensitive data!)
scope.setExtra('query', ctx.request.query);
scope.setExtra('variables', ctx.request.variables);
if (err.path) {
// We can also add the path as breadcrumb
scope.addBreadcrumb({
category: 'query-path',
message: err.path.join(' > '),
level: Sentry.Severity.Debug,
});
}
Sentry.captureException(err);
});
}
},
};
},
};
};
export const server = new ApolloServer({
schema,
context,
plugins: [sentryPlugin],
});
Bugsnag
無料プラン
250エラー/1d
slack連携あり
導入方法
上記のSentryの例をBugsnagに書き換えた
export const bugsnagPlugin = (): ApolloServerPlugin => {
return {
async requestDidStart(_) {
/* Within this returned object, define functions that respond
to request-specific lifecycle events. */
return {
async didEncounterErrors(ctx) {
log.error('error!!');
// If we couldn't parse the operation, don't
// do anything here
if (!ctx.operation) {
return;
}
for (const err of ctx.errors) {
// Only report internal server errors,
// all errors extending ApolloError should be user-facing
if (err instanceof ApolloError) {
continue;
}
Bugsnag.notify(err, (event) => {
event.addMetadata('graphql', {
operation: ctx.operation?.operation,
query: ctx.request.query,
variables: ctx.request.variables,
errorPath: err.path?.join(' > '),
});
// contextにuserIdを詰めている場合は以下のような感じで取得できる
// ここで言うcontextとはhttps://www.apollographql.com/docs/apollo-server/data/resolvers/#the-context-argument を指す
event.addMetadata('user', {
fuid: ctx.context.useId,
});
});
}
},
};
},
};
};
採用しなかった導入方法
https://www.npmjs.com/package/apollo-server-plugin-bugsnag を使う
採用しなかった理由
- ユーザー由来のエラーの場合はBugsnagに送らないといった制御が出来なそうだった
- 採用しなくても上記の導入方法のように書けば十分シンプルなコードで動くため
最終的に採用したエラー検知ツール
Bugsnagにした
理由
無料枠でslack integration がついてくるため
このスクラップは2022/01/21にクローズされました