はじめに
EC2でWebサイトのエラーログを受け取る時に、CloudWatch -> SNSだと、英語がよくわからなくて萎えるので、Lambdaで整形してからSNSでメール送信したい。
Cloudwatchlogs -> lambda -> SNS
という構成でやる。
他にも、Cloudwatchlogs -> kinesis -> lambda -> SNS
という方法があるが、kinesisを挟んでもあんまり旨みがないし(もちろんメリットが0ではない)、kinesis -> lambda
はポーリングなので、イベントドリブンにできない欠点がある。
参考
【新機能】Amazon CloudWatch LogsのログデータをAWS Lambdaでリアルタイムに処理する
CloudWatch Logs Subscriptionsを利用したZabbixへのログ転送
構成
-
EC2 -> CloudwatchLogs
は、エージェントを経由して行う - データはloggroupでグループ化して管理する。(通知の際の切り分けがしやすいように)
-
CloudwatchLogs -> lambda
はsubscriptionを使って、ERROR,FATALだけに絞る(INFO,WARNを送るといっぱいリクエスト増えてお金かかるので) - lambdaはpythonで書く(簡単なので)
-
CloudwachLogs -> SNS
はメトリックスフィルターを使う - lambdaのエラー処理も行う(心配なので)
- 今回SNSの整形は頑張らない(必要な情報がカスタマイズできる感じがわかればいい)
料金
cloudwatchlogs
項目 | 単位 | 料金(Month) |
---|---|---|
カスタムメトリックス | 個 | $0.5 |
アラーム | 個 | $0.10 |
取り込み | GB | $0.76 |
アーカイブ | GB | $0.033 |
EC2からの転送 | GB | $0.140 |
EC2からの転送は最初の1GB無料、次の10Tまでがこの値段
エラーの頻度にもよるが、そんなにお金はかからない印象。
1時間に1回くらいの頻度でエラーが吐かれるくらいなら、2ドル/月もかからないと思う
lambda
リクエストのうち毎月最初の 1,000,000 件は無料
今回の構成では無料でいけると思う。
SNS
Emailは1000件無料。
その後は100,000 件あたり 2 ドル
今回は、エラーがいっぱい出たとしても2ドルで済むと思う。
合計
2ドル(cloudwatchlogs) + 無料(lambda) + 2ドル(SNS) = 4ドル
もちろん、cloudwatchlogsのデータ保持容量、エラーログ頻度によっていろいろ変わります。
コード
- lambda
from __future__ import print_function
import base64
import json
import zlib
import datetime
import boto3
sns = boto3.client('sns')
print('Loading function')
def lambda_handler(event, context):
#print("Received event: " + json.dumps(event, indent=2))
data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS)
data_json = json.loads(data)
log_json = json.loads(json.dumps(data_json["logEvents"][0], ensure_ascii=False))
if data_json["logGroup"]:
date = datetime.datetime.fromtimestamp(int(str(log_json["timestamp"])[:10])) + datetime.timedelta(hours=9)
sns_body = {}
sns_body["default"] = ""
sns_body["default"] += "Owner : " + data_json["owner"] + "\n"
sns_body["default"] += "LogGroup : " + data_json["logGroup"] + "\n"
sns_body["default"] += "LogStream : " + data_json["logStream"] + "\n"
sns_body["default"] += "SubscriptionFilters : " + ''.join(data_json["subscriptionFilters"]) + "\n"
sns_body["default"] += "Time : " + date.strftime('%Y-%m-%d %H:%M:%S') + "\n"
sns_body["default"] += "Message : " + log_json['message'] + "\n"
topic = 'arn:aws:sns:ap-northeast-1:111111111111:***********'
subject = 'Your Email Subject!!'
region = 'ap-northeast-1'
response = sns.publish(
TopicArn=topic,
Message=json.dumps(sns_body, ensure_ascii=False),
Subject=subject,
MessageStructure='json'
)
return 'Successfully processed {} records.'.format(len(event['awslogs']))
注意点: 複数ログがきても対応できるように書かないといけない模様
@ryonext ご指摘ありがとうございます。
これをlambdaにポチッと貼り付ければ大丈夫です。
SNSのTOPICだけは各自変更してください。
最後に
何か間違いあればご指摘ください。