SendGridのEmail ActivityをS3に保存してAthenaでクエリする

2020.09.23

SendGridにはEmail Activityというアカウントとサブユーザーに関連付けられた最近のメール関連のアクティビティイベントのスナップショットを表示する機能があります。

メールを送信した、受信者のメールボックスに届いた、バウンスした、スパムレポートされたなどの情報を日付やメールアドレスで検索できます。

Email Activityは、freeプランの場合だと3日間となっています。 有料のプランでも最大7日ですが、アドオンを購入することで30日まで保存できるようになります。 さらに古いイベントを検索したい、他のデータと組み合わせて分析したいといったことが出てくることもあるかと思います。 そういった時はEvent Webhookを利用して外部のストレージ等にデータを保存することで可能になります。

イベントは、メールがSendGridおよびメールサービスプロバイダーによって処理されるときに生成されます。 イベントには、

  • 配信イベント(Processed, Dropped, Delivered, Deferred, Bounce, Blocked)
  • エンゲージメントイベント(Open, Click, Spam Report, Unsubscribe, Group Unsubscribe, Group Resubscribe)

の2種類があります。

SendGridでEvent Webhookの設定をするには、Settings->Mail Settings->Event Webhookをクリックし、 HTTP Post URLの入力とHookしたいEventを設定します。

やってみる

LambdaとAPI Gatewayを使ってHTTP Post URLを作り、Event Webhookを処理できるようにします。

LambdaでEvent Webhookのペイロードを処理する

まず、Lambdaでeventを受信できるか確認してみます。

関数を作成し、以下のコードを保存します(node12.xのランタイムを使用した例です)

exports.handler = function(event, context) {
    console.log('--- sendgrid event start ---');
    console.log(event);
    console.log('--- sendgrid event end ---');
};

POSTで受ける口はAPI Gatewayにします。関数のトリガーに追加します。

HTTP APIにしてみました。JWTオーソライザーを使った方が良いですが、今回の検証ではオープンにして追加しています。

追加が完了した後、APIのエンドポイントをコピーします。

コピーしたURLをSendGridのEvent WebhookのURLに指定します。 Test Your Integrationのボタンをクリックし、正常にサンプルイベントが送信されたかどうか確認します。

※ Event Webhook StatusをEnableにしましょう

サンプルイベントが送信されたかどうかはcloudwatch logsで確認できます。

サンプルコードでeventを全て出力するようにしていたので、上記のようにログが吐かれているはずです。

ここまで確認できたら、Sendgrid側の設定を保存します。

全てのイベントをwebhookの対象にしました。

実際にメールを送ってみる

SendGridからのイベント情報はbodyに格納されるので、Lambdaの関数を以下のように編集します。

exports.handler = function(event, context) {
    console.log('--- sendgrid event start ---');
    console.log(event["body"]);
    console.log('--- sendgrid event end ---');
};

実際に何かメールを送信して、Cloudwatch Logsに出力されるか確認します。

設定が正しいと、ProcessedOpenなどのイベントがログに出力されることを確認できるはずです。

S3に保存するようにLambdaを編集

きちんとWebhookを受信できることが確認できたので、その内容をS3に保存します。 バケットは何か用意し,Lambdaの実行ロールにs3へのアクセス権限を付与しておきましょう。

uuidを使用するので

npm install uuid

を実行してから、node_modulesを含んだ状態で関数をデプロイします。

関数は以下のように編集します。

const AWS = require('aws-sdk')
var s3Bucket = new AWS.S3( { params: {Bucket: "<<Your Bucket>>"} } );
var uuid = require('uuid');

exports.handler = (event, context, callback) => {
    console.log('--- sendgrid event start ---');
    console.log(event.body)
    console.log('--- sendgrid event end ---');
    
    let webhook_body = "";
    let event_object = JSON.parse(event.body);
    event_object.map((item)=>{
        webhook_body = webhook_body + JSON.stringify(item) + "\n"
    }) 

    var filePath = "<<Path to Object>>/"+ uuid.v4();
    var data = {
        Key: filePath, 
        Body: webhook_body
    };
    s3Bucket.putObject(data, function(err, data){
        if (err) { 
            console.log('Error uploading data: ', data);
            callback(err, null);
        } else {
            console.log('Successfully uploaded the response');
            callback(null, data);
        }
    });
};

<<Your Bucket>><<Path to Object>>は自身の環境に合わせて変更してください。

関数をデプロイ後にメールを送信すると、以下のようにバケットにファイルが保存されます。

Athenaでクエリしてみる

S3にjsonファイルとしてイベントを保存したので、Athenaでクエリできるようにしていきます。

テーブルの作成が必要になりますが、AWS Glueのクローラーを使ってテーブルを作ります。

AWS Glue コンソールでのクローラの使用を参考に、

ファイルを保存したS3バケットを対象にしたクローラーを作成します。

このようなスキーマをもったテーブルが作成されます。

Athenaのコンソールでクエリしてみます。

SELECT * FROM "sendgrid"."sendgrid_webhooks" limit 10;

上記のような感じで10件出力されていることが確認できます。

eventやemail,ipなんかで条件を絞って検索することも可能ですね。

s3にファイルとして保存しておくことで、過去のイベントを検索したり、他のデータと組み合わせて分析することが容易になります。 SendGridのEmail Activityが30日まででは要件を満たせないといった方はこのような方法を試す価値はあるのではないかと思います。