CloudWatchLogsを監視してSlack複数チャンネルに通知するSA

# はじめに

最近、アプリケーションをECS(Fargate)で動かすことが増えてきました。
Fargateを使う場合、(firelensを使わなければ)基本的にログはCloudWatch Logsに出力すると思います。
そのログをキーワード監視したかったのですが、コレだ！！っていう機能・やり方が見つからなかったので、
SA(Serverless Application)を自作してSAR(Serverless Application Repository)に登録してみました。
この記事は、その紹介です。

# 求める機能

CloudWatch Logsをキーワード監視してSlack通知する方法を調べてみると、[この記事](https://dev.classmethod.jp/articles/aws-chatbot-slack-notification-cdk/)にあるような
`メトリクスフィルター` `CloudWatch Alarm` `AWS Chatbot`を組み合わせる方法が見つかります。
その方法でもキーワード監視はできますが、運用を想定すると以下のような機能があった方が
運用しやすいと考えました。

* ログ毎に監視するキーワード(フィルタパターン)を指定できる
* パターンマッチしたメッセージを含めて通知できる
* 複数のログを監視できる
    * リソース(LambdaやCloudWatchAlarmなど)追加なしに、監視対象を追加できる
    * 視認性のために、ログごとに通知先チャンネルや通知ユーザ名／アイコンなどが変えられる

探してないなら、、ということで作りました。

#  cloudwatch-logs-to-slack-multi紹介

SARのURLは[こちら](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:ap-northeast-1:524326266976:applications~cloudwatch-logs-to-slack-multi)

## 機能概要

CloudWatch Logsにはサブスクリプションフィルタという機能があります。 [公式](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/Subscriptions.html)
この機能はメトリクスフィルタと同様にフィルタパターンを指定でき、パターン一致した結果をKinesisや
Lambdaなど他のサービスに配信することができます。また、複数ロググループに同じ配信先を指定する
ことができます。

この機能を使用して、監視したいロググループごとにサブスクリプションフィルタを作り、配信先として
Lambda関数に向けることで、設定内容に従ってSlack通知することができます。

設定は、パラメータストアにJSON形式で保存します。
ロググループ名(前方一致条件)毎に、Slack通知に使うWebhookURL、チャンネル、ユーザ名、アイコン
などを切り替えて通知することができます。

## 処理フロー

![cwlogs-to-slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/a40b481a-be77-7e12-4b92-04c68f38c5dc.png)

## 通知例

例1/2で分かる通り、通知先チャンネルの他にも、ユーザ名、アイコン、カラーを変更できます。
これにより、エラー発生しているアプリ、環境、緊急度、などを判別しやすくなります。

### 例１
<img width="500" alt="warn.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/3abd02c5-e7ef-c06d-8c9a-6fcc6f8f91ac.png">

### 例2
<img width="500" alt="err.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/bbec5ba2-7633-8769-1780-a33b05c0f418.png">

##### おまけ)ログ直リンク機能について

通知メッセージ中の`jump to log`をクリックするとマネコンのCloudWatchLogsの当該ログに直接
飛ぶことができます。
通知されたログがオレンジ色にハイライトされた状態で表示されます。
<img width="500" alt="jumptolog.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/f4e07ece-41a3-1495-6e88-323f8dee137d.png">

## 導入手順

### 1. パラメータストアでパラメータ作成

`/lambda/CWLogsToSlack/Configuration` という名前でパラメータを作成します。
（パラメータ名は任意ですが、スラッシュで始まる必要があります）
StringでもSecureStringどちらでもOKです。

```json:/lambda/CWLogsToSlack/Configuration
{
  "default": {
    "hook_url":"https://hooks.slack.com/services/HOGEHOGEH/****",
    "channel":"{デフォルト通知先チャンネル}",
    "username":"{デフォルト通知ユーザ名}",
    "icon_emoji":"{デフォルトアイコン(例 :bow:)}",
    "color": "{デフォルトカラー(例 #D00000)}"
  },
  "rules": [
    {
      "if_prefix": "{`ロググループ：ログストリーム`の前方一致条件}",
      "hook_url":"{上書きするWebhookURL}",
      "channel":"{Specify when overwriting}",
      "username":"{Specify when overwriting}",
      "icon_emoji":"{Specify when overwriting}",
      "color": "{Specify when overwriting}"
    },
　　 <ここにif_prefixの条件数分追加>
  ]
}
```

if_prefixは 処理対象ログの{ロググループ:ログストリーム}という文字列を前方一致で判定し、
真であればdefault設定を個別設定(rules配列の要素のこと)で上書きした設定でSlack通知されます。
※個別設定ごとに、`if_prefix`と`hook_url`は必須

#### Webhookの注意点

SlackのIncomingWebhookは２種類あります。
`Custom Integrationから作成するWebhook`と、`Slack Appから作成するWebhook`です。
前者であればchannel/username/ucon_emojiを上書き可能ですが、後者のWebhookの場合だと
Webhook作成時に指定した値が固定となり上書きすることができません。
（JSONで指定しても無視されます。[README.md](https://github.com/miyaz/cloudwatch-logs-to-slack-multi/blob/master/README-ja.md#slack%E9%80%9A%E7%9F%A5%E3%81%AE%E3%81%9F%E3%82%81%E3%81%ABincoming-webhook-url%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)も参照）

ちなみにCustom Integrationの方は非推奨らしいのでご利用は計画的に。
（上書きできたりいろいろ便利ですが、権限強すぎな気もしますね）

### 2. Lambda関数作成画面でSARを検索

検索フィールドに、`slack logs`を入力し、`カスタムIAMロールまたは〜` のチェックボックスを入れて
検索すると`cloudwatch-logs-to-slack-multi`という名前で表示されるのでリンクをクリックします。

<img width="800" alt="sarsearch.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/4e54e267-950f-8203-bf79-47fca7f5901e.png">

### 3. デプロイ

`このアプリがカスタム IAM ロールを作成することを承認します。`のチェックを入れてデプロイ実行！
（デフォルトから変えた場合はパラメータ名を修正してください）

<img width="400" alt="deploy.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/1bc88454-5c9a-4129-ceca-7078ce8dc3c1.png">

しばらく待って以下の表示になれば完了です。
<img width="350" alt="deploy.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/416465b1-4ad0-160f-519b-617a1447fd41.png">

## ログ監視設定手順

### 1. CloudWatchLogs ロググループにサブスクリプションフィルタ設定

任意のロググループを選択し、アクションから`Lambdaサブスクリプションフィルターを作成`をクリック

<img width="350" alt="action.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/50f4e8ba-fc21-9621-ddb6-cbd989bff574.png">

### 2. Lambda関数を指定

`CWLogsToSlack`を選択し、フィルタパターンを指定して`ストリーミングを開始`ボタンをクリックします。
開始するとすぐにログ監視が始まり、フィルタにマッチしたログが通知されるようになります。

<img width="500" alt="select.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51108/809b4177-e290-136f-29e2-5f9fc6d7299d.png">

監視が必要なロググループ分上記を繰り返し実施します。

これで設定は完了です。

## 注意点

* パラメータストアの設定を変更してもLambda側に即時反映されるわけではありません
    * コンテナ入れ替えを待つか、関数に変更加えて保存するか（関数入れ替える方法ってないのかなー）
* Lambdaの同時実行数は1固定になります
    * そもそもログ監視で大量に通知されるのは異常時なので、予め絞っています

# まとめ

今後もコンテナ化の流れは止まらないので、個人的に運用しやすいログ監視の仕組みを作れて満足。
まだまだ改善点はありそうなので、しばらく運用しつつ改善していきます。
そもそもCloudWatchLogsを活用するための機能が足りていない気がするので、AWS公式で出てくるかも
しれませんが、それが充実するまでは自分で隙間を埋めつつベストな仕組みを追求していこうと思います！

あと今回初めてSARに登録してみましたが、aws-sam-cliで簡単に登録できました。
今後さらにSARに有用な機能が増えていくと思うので要チェックですね！