はじめに
エラーログ。。簡単にキーワードでひっかけてSlack通知させたいなぁ。。
そんなお悩みありませんか?
AWS上で運用されているかた必見!
かーんたんに(そしてわりと安く)ログ監視できちゃう方法をお披露目したいと思います!
構成
やり方
- CloudWatchAgentをEC2に導入する
- Lambdaの処理を書く
- ロググループにサブスクリプションフィルターを設定する
1. CloudWatchAgentをEC2に導入する
ポリシーが必要になるので、以下を参考にアタッチしてください。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/create-iam-roles-for-cloudwatch-agent.html#create-iam-roles-for-cloudwatch-agent-roles
インストールはAmazonLinux2を利用してる場合以下で一発導入可能。
$ sudo yum install amazon-cloudwatch-agent
その他のサポート対象OSをお使いの場合はこちらを参考に導入してみてください!
サポート対象OSについては以下をご確認ください。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html
導入が済んだら、設定ファイルを書いて起動してみましょう!
起動方法はOSによって異なるので、OSに合わせた起動方法で起動してください。
設定例
{
"agent": {
"run_as_user": "root"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/hoge_service/fuga.log",
"log_group_name": "/aws/ec2/hoge_service/fuga.log",
"log_stream_name": "{instance_id}"
}
]
}
}
}
}
log_stream_nameはinstance_id
を使うと手っ取り早くEC2毎にログストリームをわけてくれてオススメ設定です。
複数サーバーで使う場合は同じストリームにしないようにした方が良いでしょうね。
(理由は。。試せばわかりますw)
サーバー毎に監視したいエラーメッセージなどが異なる場合はlog_group_nameをサーバー毎にわける必要があります。
例: サーバーAではHogeErrorを監視する必要があって、サーバーBではFugaErrorを監視する必要がある場合は、サーバーAとサーバーBでlog_group_nameを分ける必要がある。
CloudWatchAgentはこの他にも色々な事ができて便利なので、気になる方は試してみてください。
2. Lambdaの処理を書く
CloudWatchLogsからトリガーされてSlack通知をするちょうど良いサンプルがPythonであったので、Pythonにしてます。
Lambdaがサポートしているお好きな言語で書くと良いと思います。
尚、Slackへの通知はIncoming webhookを想定して書いてます。
Incoming webhookの使い方は各自お調べください。
ログストリーム名をEC2のインスタンスIDとしているのでAWSコンソール上のEC2詳細画面へのリンクを生成して通知メッセージに入れてます。(オススメ!!)
またロググループへの遷移もできるようにしているので、前後のログ状況が通知後すぐに確認できます。(オススメ!!)
import base64
import requests, json
import urllib
import zlib
import datetime
import os
import boto3
import pprint
from botocore.exceptions import ClientError
print('Loading function')
def lambda_handler(event, context):
data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS)
data_json = json.loads(data)
WEB_HOOK_URL = "https://hooks.slack.com/services/hoge/fuga/XXXXXXX"
WIKI_LINK_TITLE = "<https://hoge.fuga/wiki/HogeError|WIKI>"
for log in data_json["logEvents"]:
log_json = json.loads(json.dumps(log, ensure_ascii=False))
#print(log_json)
LOG_GROUP_LINK_TITLE = "<https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#logsV2:log-groups/log-group/%s/log-events/%s|%s>" % (urllib.parse.quote_plus(data_json['logGroup']),data_json['logStream'],data_json['logGroup'])
INSTANCE_ID_LINK_TITLE = "<https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#InstanceDetails:instanceId={0}|{0}>".format(data_json['logStream'])
print("LOG_GROUP : %s" % data_json['logGroup'])
print("STREAM_NAME : %s" % data_json['logStream'])
try:
message = u':no_entry:HOGEエラーが発生しました。\nサーバーの状態を確認して%sを参考に対応してください。\nロググループ名:%s\nインスタンスID:%s\nエラーメッセージ:\n```\n%s\n```' % (WIKI_LINK_TITLE,LOG_GROUP_LINK_TITLE,INSTANCE_ID_LINK_TITLE,log_json['message'])
requests.post(WEB_HOOK_URL, data = json.dumps({
'channel': u'#XXXXXXX',
'text': message, #通知内容
'username': u'Alart From AWS lambda' #ユーザー名
}))
except Exception as e:
print(e)
print('End function')
requestsライブラリが外部パッケージとなっているため、pipなどでダウンロードしてzipで固めてuploadする必要があります。
以下Mac上で対応した例です。
最後にlambda_function.pyを忘れずに入れてディレクトリごとzipで固めてuploadすれば幸せになれます。
$ mkdir for_lambda
$ cd for_lambda/
$ pip3.7 install requests -t .
Collecting requests
Downloading https://files.pythonhosted.org/packages/39/fc/f91eac5a39a65f75a7adb58eac7fa78871ea9872283fb9c44e6545998134/requests-2.25.0-py2.py3-none-any.whl (61kB)
|████████████████████████████████| 61kB 7.1MB/s
Collecting chardet<4,>=3.0.2
Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Collecting urllib3<1.27,>=1.21.1
Downloading https://files.pythonhosted.org/packages/f5/71/45d36a8df68f3ebb098d6861b2c017f3d094538c0fb98fa61d4dc43e69b9/urllib3-1.26.2-py2.py3-none-any.whl (136kB)
|████████████████████████████████| 143kB 9.9MB/s
Collecting certifi>=2017.4.17
Downloading https://files.pythonhosted.org/packages/5e/a0/5f06e1e1d463903cf0c0eebeb751791119ed7a4b3737fdc9a77f1cdfb51f/certifi-2020.12.5-py2.py3-none-any.whl (147kB)
|████████████████████████████████| 153kB 10.2MB/s
Collecting idna<3,>=2.5
Using cached https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl
Installing collected packages: chardet, urllib3, certifi, idna, requests
Successfully installed certifi-2020.12.5 chardet-3.0.4 idna-2.10 requests-2.25.0 urllib3-1.26.2
$ ll
total 0
drwxr-xr-x 3 hoge_fuga 1189740132 96 12 12 06:58 bin
drwxr-xr-x 7 hoge_fuga 1189740132 224 12 12 06:58 certifi
drwxr-xr-x 8 hoge_fuga 1189740132 256 12 12 06:58 certifi-2020.12.5.dist-info
drwxr-xr-x 43 hoge_fuga 1189740132 1376 12 12 06:58 chardet
drwxr-xr-x 10 hoge_fuga 1189740132 320 12 12 06:58 chardet-3.0.4.dist-info
drwxr-xr-x 11 hoge_fuga 1189740132 352 12 12 06:58 idna
drwxr-xr-x 8 hoge_fuga 1189740132 256 12 12 06:58 idna-2.10.dist-info
drwxr-xr-x 21 hoge_fuga 1189740132 672 12 12 06:58 requests
drwxr-xr-x 8 hoge_fuga 1189740132 256 12 12 06:58 requests-2.25.0.dist-info
drwxr-xr-x 17 hoge_fuga 1189740132 544 12 12 06:58 urllib3
drwxr-xr-x 8 hoge_fuga 1189740132 256 12 12 06:58 urllib3-1.26.2.dist-info
$ touch lambda_function.py
3. ロググループにサブスクリプションフィルターを設定する
CloudWatchAgentの設定がうまくいってログが転送され出すと、CloudWatchLogsに設定ファイルに設定したロググループ名でロググループが追加されていると思います。
今すぐAWSコンソールを開いて確認してみましょう!
必要に応じてログの保持期間などを変更しておきましょう。
ロググループの一覧からでも詳細からでも構いませんので、アクションからLambda サブスクリプションフィルターを作成を選択します。
以下を参考に設定してみましょう。
一つのロググループに対して最大で二つのサブスクリプションフィルターが作成できます。(サポートにて確認済み)
また、面倒ですが、一度登録したサブスクリプションフィルターはLambda側でのみ削除できます。
AWS CLIを使えばこの限りではないと思います。(そこまでは調べてないです。。)
うまく連携できるとLambda側で以下のように確認できます。
ここまでできれば、Slack通知されるはずです!
できてなかったら、Lambdaの実行ログなどを確認してトラブルシューティング頑張ってください!
おわりに
比較的コーディング少なめでログの監視ができるものです。
ElasticSearch版のサブスクリプションフィルターもあるようなので、より高度なログ解析のできるようです。
機会があれば、チャレンジしてみたいものです^^