[CDK]Sentry無料プランでSlackにエラー通知する方法

2021.10.27

吉川@広島です。

SentryのSlack連携機能を利用するには有料プランを契約する必要があります(2021/10現在)。

それでも無料プランで通知する方法はあって、Webhook通知する機能は解放されているため、AWS APIGateway+LambdaでSentryからの通知を受け、そこからSlackに通知する方法を紹介します。

frontendのエラーをSlack通知したい

こちらの記事をかなり参考にさせて頂きました。

なお、SlackのIncoming Webhook設定は割愛しています。ご留意ください。

環境

  • Node.js 14.16.1
  • npm 8.1.1
  • aws-cdk 1.129.0
  • typescript 4.4.4
  • @slack/webhook 6.0.0
  • @types/aws-lambda 8.10.84

CDKコード

AWSリソースはCDKで用意します。CDKプロジェクトの雛形を構築します。

mkdir cdk-pj
cd cdk-pj
npx aws-cdk init --language typescript
npm i -S @aws-cdk/aws-{lambda,apigateway}

CDKコードを書いていきます。

// bin/cdk.ts

#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from '@aws-cdk/core'
import { CdkStack } from '../lib/cdk-stack'

const app = new cdk.App()
new CdkStack(app, 'sentry-alarm-stack', {
  slackWorkspaceId: 'xxxxxxxxxxx',
  slackChannelId: 'xxxxxxxxxxx',
})
// lib/cdk-stack.ts
import * as cdk from '@aws-cdk/core'
import * as apigw from '@aws-cdk/aws-apigateway'
import * as lambda from '@aws-cdk/aws-lambda'

export interface MyProps {
  readonly slackChannelId: string
  readonly slackWorkspaceId: string
}

export class CdkStack extends cdk.Stack {
  constructor(
    scope: cdk.Construct,
    id: string,
    props?: cdk.StackProps & MyProps
  ) {
    super(scope, id, props)

    /**
     * APIGateway
     */
    const restApi = new apigw.RestApi(this, 'apigw', {})

    /**
     * Lambda
     */
    const fn = new lambda.Function(this, 'fn', {
      runtime: lambda.Runtime.NODEJS_14_X,
      code: lambda.Code.fromAsset('/path/to/dist'), // 後述するBundle済LambdaコードのPATHを指定
      handler: 'index.handler',
      environment: {},
    })
    restApi.root.addMethod('POST', new apigw.LambdaIntegration(fn))
  }
}

Lambdaコード

こちらは特に冒頭紹介した記事のコードを参考にさせて頂いています。一部自分用にアレンジしたのと、TypeScript対応などを加えました。

npm i -S @slack/webhook
npm i -D typescript @types/node @types/aws-lambda esbuild
// src/index.ts

import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'
import { IncomingWebhook, IncomingWebhookSendArguments } from '@slack/webhook'

// Slack Webhook URLをセット
const url =
  'https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxxx'
const webhook = new IncomingWebhook(url)

export const handler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  console.log(JSON.stringify({ event }))

  /**
   * Sentryからのエラー情報をまとめる
   */
  const sentryBodyStr = event.body!
  const sentryBody = JSON.parse(sentryBodyStr)
  const sentryEvent = sentryBody.event

  const browserInfo =
    sentryEvent.contexts.browser != null
      ? `${sentryEvent.contexts.browser.name} ${sentryEvent.contexts.browser.type} ${sentryEvent.contexts.browser.version}`
      : 'unknown'

  const osInfo =
    sentryEvent.contexts.os != null
      ? `${sentryEvent.contexts.os.name} ${sentryEvent.contexts.os.type} ${sentryEvent.contexts.os.version}`
      : 'unknown'

  const deviceInfo =
    sentryEvent.contexts.device != null
      ? sentryEvent.contexts.device.family
      : 'unknown'

  const title = sentryEvent.title

  const url = sentryBody.url

  const env = sentryEvent.environment

  const issue = {
    message: title,
    detail: url,
    browser: browserInfo,
    os: osInfo,
    device: deviceInfo,
  }
  const issueProperties = Object.entries(issue).map((entry) => {
    return `${entry[0]}: ${entry[1]}`
  })

  /**
   * Slack Webhookに送信
   */
  const payload: IncomingWebhookSendArguments = {
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `*${env}* ERROR Notification`,
        },
      },
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: issueProperties.join('\n'),
        },
      },
    ],
  }
  await webhook.send(payload)

  return {
    statusCode: 200,
    body: '',
  }
}
npx esbuild src/index.ts --platform=node --target=node14 --bundle --outfile=dist/index.js

Sentryの設定手順

CDKで生成したAPIGatewayのエンドポイントURLを控えておきます。

Project Settings→左のメニューのWebhooksを選択し、Enable Pluginを押下します。

Callback URLsにAPIGatewayのエンドポイントURLを貼り付けて、Save Changesを押下します。そして、Test Pluginを押すことでテストリクエストを送信できます。

するとこのようにSlackにテスト通知がいくことを確認できました。

最後に、実際にエラーが発生した際に通知する設定をします。

Project Details→Create Alertを押下します。

Issuesを選択したまま、Set Conditionsを押下します。

Alert nameに任意のアラート名を入力します。そして、THENにSend a notification via Webhooksを設定しましょう(ここが大事です)。

以上で設定完了です。

最後に

Webhookを使ってがんばって無料で使おうとすることに対して「開発に利用させてもらってるんだから対価を払うべき」や「APIGateway+Lambdaの構築・保守の手間が増えるので、それなら課金した方が合理的」というご意見もあるかもしれません。

その通りなのですが、「導入して効果がどれくらいあるかわからないからまずは一定期間無料で試してみたい」といった場合には良さそうかと思いました。