MailgunのstatsをDatadogに流すLambda関数を作った

Mailgunを使い始めました。すでにDatadogを利用しています。

Mailgunのメトリクスを眺めてると、これどうにかDatadogに流し込んで、監視できねーかなと。もやっと眺めてたら、このあたり接続したら、いい感じになりそうです。

* Mailgunは、 StatsというAPIから、HTTP GETで、最小１時間刻みの件数を取得できます
    * https://documentation.mailgun.com/en/latest/api-stats.html
* Datadogは、独自のメトリクスをAPIからHTTP POSTで投入できます
    * https://docs.datadoghq.com/ja/api/?lang=console#metrics-post
* Datadogは、メトリクスがいつもと違う動きをした時に通知してくれるような監視を構築できます
    * https://docs.datadoghq.com/ja/guides/anomalies/
* AWS Lambdaは、CloudWatch Eventで、cron式などでスケジュール実行することができます
    * https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html

## 情報収集

* Mailgunのドメイン名。
    * 情報収集したいドメイン名。Stats APIは、ドメイン単位で集計されてるので。
* MailgunのAPIキー。
    * Stats APIから情報収集するのに使います。
    * https://mailgun.com/app/dashboard にドーンと出てる、プライベートAPIキー。
* DatadogのAPIキー
    * Datadogにメトリクスをpostするために使います。
    * https://app.datadoghq.com/account/settings#api からAPIキーを。

## Lambdaファンクション

* ランタイムはPython3.x
* 環境変数３個。さきほど収集した情報を、以下の環境変数にそれぞれ設定します。
    * MAILGUN_DOMAIN
    * MAILGUN_API_KEY
    * DATADOG_API_KEY
* 実行時間は1分あれば大丈夫。実績13秒前後。
* メモリは128MBで大丈夫。実績25MB程度。
* 起動トリガーはCloudWathc Eventの ```rate(1 hour)```
 
ファンクションのコードは以下。Lambdaのインライン編集で管理したいので、標準ライブラリ縛りで書いてます。

```python
import os
import urllib.request
import urllib.parse
import base64
import json
import time

now = time.time()

MAILGUN_DOMAIN=os.environ["MAILGUN_DOMAIN"]
MAILGUN_API_KEY=os.environ["MAILGUN_API_KEY"]
DATADOG_API_KEY=os.environ["DATADOG_API_KEY"]

def get_mailgun_stats():
    # https://documentation.mailgun.com/en/latest/api-stats.html
    url = "https://api.mailgun.net/v3/" + MAILGUN_DOMAIN + "/stats/total?event=accepted&event=delivered&event=failed&duration=1h"
    # MailgunのAPIはBasic認証がかかっている。APIキーがBasic認証キー。
    basic_auth_parm = 'api:' + MAILGUN_API_KEY
    basic_user_and_pasword = base64.b64encode(basic_auth_parm.encode('utf-8'))
    request = urllib.request.Request(url, method="GET", headers={"Authorization": "Basic " + basic_user_and_pasword.decode('utf-8')})
    with urllib.request.urlopen(request) as response:
        return json.loads(response.read().decode('utf-8'))

def post_datadog_metrics(name, value):
    # https://docs.datadoghq.com/ja/api/?lang=console#metrics-post
    url = "https://api.datadoghq.com/api/v1/series?api_key=" + DATADOG_API_KEY
    method = "POST"
    headers = {"Content-Type" : "application/json"}
    obj = {
        "series": [
            {
                "metric":name,
                "points":[
                    [now, value]
                ],
                "type":"gauge",
                "tags":"[mailgun-domain:" + MAILGUN_DOMAIN + "]"
            }
        ]
    }
    json_data = json.dumps(obj).encode('utf-8')
    request = urllib.request.Request(url, data=json_data, headers=headers, method="POST")
    with urllib.request.urlopen(request) as response:
        return response.read().decode("utf-8")

def lambda_handler(event, context):
    mailgun_stats_full = get_mailgun_stats()
    # failedが他と異なり階層が深くなってるためにこんな面倒に
    mailgun_stats = {}
    mailgun_stats['accepted'] = mailgun_stats_full['stats'][0]['accepted']
    mailgun_stats['delivered'] = mailgun_stats_full['stats'][0]['delivered']
    mailgun_stats['failed-permanent'] = mailgun_stats_full['stats'][0]['failed']['permanent']
    mailgun_stats['failed-temporary'] = mailgun_stats_full['stats'][0]['failed']['temporary']
    # ループ二階層は辞めたいけど動くから良いことにする
    for metrics_category_name, metrics_kv in mailgun_stats.items():
        for metrics_name, metrics_value in metrics_kv.items():
            metrics_full_name = 'mailgun-' + metrics_category_name + '-' + metrics_name
            post_datadog_metrics(metrics_full_name, metrics_value)
            print('%s : %d' %(metrics_full_name, metrics_value))
```

## Datadogダッシュボード

Datadogに以下の名前でメトリクスを投げます。ついでに ```mailgun-domain: ......``` というタグも付けているので、必要ならご活用を。

* mailgun-accepted-incoming
* mailgun-accepted-outgoing
* mailgun-accepted-total
* mailgun-delivered-smtp
* mailgun-delivered-http
* mailgun-delivered-total
* mailgun-failed-permanent-suppress-bounce
* mailgun-failed-permanent-suppress-unsubscribe
* mailgun-failed-permanent-suppress-complaint
* mailgun-failed-permanent-bounce
* mailgun-failed-permanent-delayed-bounce
* mailgun-failed-permanent-total
* mailgun-failed-temporary-espblock
* mailgun-failed-temporary-total

Datadogのダッシュボードは、こんなグラフにして見えるようにしてます。

```json
{
  "requests": [
    {
      "q": "avg:mailgun_accepted_incoming{*}, avg:mailgun_accepted_outgoing{*}, avg:mailgun_delivered_http{*}, avg:mailgun_delivered_smtp{*}, avg:mailgun_failed_permanent_bounce{*}, avg:mailgun_failed_permanent_delayed_bounce{*}, avg:mailgun_failed_permanent_suppress_bounce{*}, avg:mailgun_failed_permanent_suppress_complaint{*}, avg:mailgun_failed_permanent_suppress_unsubscribe{*}, avg:mailgun_failed_temporary_total{*}",
      "type": "bars",
      "style": {
        "palette": "dog_classic",
        "type": "solid",
        "width": "normal"
      },
      "conditional_formats": [],
      "aggregator": "avg"
    }
  ],
  "viz": "timeseries",
  "autoscale": true,
  "status": "done"
}
```

## Datadogで監視

メトリクスが来ているので、あとはいつもどおりに監視設定して、通知を受けられるようにするだけです。