Anket v0.9.3 をリリースして Sentry を導入しました

Anketについて↓
blog.hatappi.me

v0.9.3のリリース内容

  • Sentryの導入

各リリース内容の詳細

Sentryの導入

sentry.io

Sentry はエラートラッキングサービスです。
アプリケーションで起きる例外などをひろって収集してくれます。
Anket では API や管理画面が Rails , Slack の SlashCommand の実装などを botkitで行っており、それぞれにSentryを導入しました。

Railsへの導入

docs.sentry.io

Railsへの導入は難しくなくて↑のドキュメントを見ながら導入することができます。

botkitへの導入

こっちがちょっと厄介でした。

botkit は SlashCommand 用のエンドポイントや認証用の Endpoint がExpress で提供されます。

controller.setupWebserver(process.env.PORT, function(err, webserver) {
  controller.createWebhookEndpoints(controller.webserver);
  controller.createOauthEndpoints(controller.webserver, function(err, req, res) {
    if (err) {
      res.status(500).send("ERROR: " + err);
    } else {
      res.send("OK");
    }
  });
});

まずはこのExpressのエラーをトラッキングする必要があります。
Express に関しても Rails と同様に公式に導入ドキュメントがあるのでそちらを参考にします。

docs.sentry.io

import * as express from 'express';
import * as Sentry from '@sentry/node';

Sentry.init({ dsn: "https://~~~~~~~", environment: process.env.NODE_ENV }); 

~~~~

controller.setupWebserver(process.env.PORT, function(err, webserver) {
  // この記載をどのミドルウェアよりも先に定義する必要あり
  webserver.use(Sentry.Handlers.requestHandler() as express.RequestHandler);
  controller.createWebhookEndpoints(controller.webserver);
  controller.createOauthEndpoints(controller.webserver, function(err, req, res) {
    if (err) {
      res.status(500).send("ERROR: " + err);
    } else {
      res.send("OK");
    }
  });
});

これで Express のほうは終わりです。
しかしbotkitはSlashCommandのリクエストを受け取ったあとのアクションやbotの場合は任意の単語にリプライするように作り込むことができます。

それらはハンドラを使って定義していきます。
↓こんな感じ

controller.hears('hello','direct_message',function(bot,message) {
  bot.reply(message,'Hello!');
});

controller.hears('^stop','direct_message',function(bot,message) {
  bot.reply(message,'Goodbye');
  bot.rtm.close();
});

例えばチームIDは例外が起きた時に出しておくと調査がはかどる可能性があるので出しておきたいですが、そのためにはハンドラの中でbotmessageから情報を取得する必要があります。
ハンドラを追加するごとにその処理を追加するのは面倒ですよね。。。

これはbotkitのMiddleware Error Eventsを拾うことで解決しました。
botkit.ai

controller.on('pipeline_error', function(err, bot, message, pipeline_stage) {
  // チームIDを取得する
  const teamId = bot.identifyTeam();

  const s = new Sentry.Hub(Sentry.getCurrentHub().getClient());
  // globalなtagsは変更せずにこのブロックないで有効なタグを定義する
  s.withScope(scope => {
    scope.setTag("teamId", teamId);
    scope.setTag("pipeline", pipleine);
    s.captureException(err);
  });

  bot.replyInteractive(message, "エラー発生!!");
});

最後に

ひとまずエラーが起きたら検知することができました!