はじめに
今回は、CloudWatchLogsで収集したログからエラーログをサブスクリプションフィルターでフィルタリングして、 Lambda関数を起動しSNSでメール発報する仕組みを構築していきます。
CloudWatchは、AWSサービスに限らず様々なリソースのログを収集できます。ですので、作成したサービスに合わせて、ログ監視をこの仕組みに切り出して使用することもできるかと思います。
また、Lambdaを介しているため、必要に応じてLambdaのコード内でエラーログを詳細にフィルタリングすることができます。
構成図

何らかのシステムのログをCloudWatchLogsのロググループに出力し、ロググループにサブスクリプションフィルターを設定します。ERRORなどの文字列でフィルタリングされたログイベントがLambdaに送信されてSNSトピックにメール送信をさせます。
事前準備
必要なリソースを準備しておきます。
擬似エラーログを発生させるCloudWatchロググループの作成
CloudWatchロググループは、CloudWatchが収集したログをグルーピングしておける機能です。ロググループという単位のフォルダを作成して、ログを溜め込んでおけるようなイメージです。
- ロググループの作成を選択
- ロググループ名は、
sample-loggroup-001
としておきます。- 別名で作成される際は、CloudFormationスタック作成時に、Defaultの値を書き換えるようにしてください。
- 保持期間は、7日にします。
- KMSkeyの指定は空白のまま作成を選択します。

SNSを作成してメールアドレスを設定
SNSはメール通知を初めとした様々な通知機能をサポートするサービスです。トピックとサブスクリプションという単位で通知対象を設定していきます。今回は、Eメール通知用のアドレスを設定します。
- トピックの作成を選択

- タイプは、スタンダードを選択
- その他の設定は変更せずトピックの作成を選択

- サブスクリプションの作成を選択

- プロトコルはEメールを選択
- エンドポイントにメールを受け取りたいEメールアドレスを入力してサブスクリプションの作成を選択
- 下記のようなメールがSNSから届くので
Confirm subscription
を選択することでメールアドレスの設定が完了する

CloudFormationで構築
CloudFormationテンプレートは以下にございます。
ダウンロードしてデスクトップなど任意のフォルダに保存します。
※その他のファイルも後述の手順で使用するためダウンロードしておきます
CloudFormation:YAMLテンプレートのポイント
YAMLテンプレートのポイントを記述します。
- Lambda
- SNS通知を実行する Lambda関数を作成しています。
- Propertiesで必要なパラメータを指定しています。
- 特に、RuntimeやHandlerは必須項目となるため、必ず指定します。
- また、Hndlerは、コード内で定義する関数名に一致していないと正しく動作しませんので注意が必要です。
- Environmentで環境変数を設定しています。
- 環境やアカウント、リージョンで固有の値は、ハードコーディングせず、
Outputs
やParameters
、あるいは、別途jsonを用意して、参照する形にします。
- 環境やアカウント、リージョンで固有の値は、ハードコーディングせず、
- Lambda IAM Role
- Lambdaに付与するロールとそのポリシーの作成と紐付けをしています。
- GUI作成時には、自動作成されますが、CloudWatchLogsロググループへのログストリームとログイベント作成権限を与えています。
- リソースも対象のロググループに絞っています。
- boto3でSNS通知したいためSNSへのアクセス権限も付与しています。
- GUI作成時には、自動作成されますが、CloudWatchLogsロググループへのログストリームとログイベント作成権限を与えています。
- Lambdaに付与するロールとそのポリシーの作成と紐付けをしています。
- CloudWatchLogs subscriptionfilter
- ログ監視対象となるロググループに対して設定しています。
FilterPattern
にてParameters
から受け取ったフィルタリングしたい文字列を指定しています。
Resources:
# ------------------------------ #
# Lambda
# ------------------------------ #
Lambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${Env}-lambda-${ResourceName}"
Architectures:
- "arm64"
MemorySize: 128
Role: !GetAtt LambdaRole.Arn
Runtime: !Ref Runtime
Handler: !Sub "${Env}-lambda-${ResourceName}.lambda_handler"
Timeout: 60
Environment:
Variables:
SNS_TOPIC_ARN: !Ref SnsTopicArn
Code:
S3Bucket: !ImportValue S3BucketNameForLambdaCode
S3Key: !Sub "${Env}-lambda-${ResourceName}.zip"
# ------------------------------ #
# Lambda IAM Role
# ------------------------------ #
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${Env}-role-lambda-${ResourceName}"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub "${Env}-policy-LambdaBasicExecutionRole-${ResourceName}"
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: CreateLogGroup
Effect: Allow
Action: logs:CreateLogGroup
Resource: !Sub "arn:aws:logs:ap-northeast-1:${AccountId}:*"
- Sid: PutLogEvents
Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub "arn:aws:logs:ap-northeast-1:${AccountId}:log-group:/aws/lambda/${Env}-lambda-${ResourceName}:*"
- PolicyName: !Sub "${Env}-policy-snspublish-${ResourceName}"
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: Publish
Effect: Allow
Action: sns:Publish
Resource: !Ref SnsTopicArn
# ------------------------------ #
# CloudWatchLogs SubscriptionFilter
# ------------------------------ #
CloudWatchLogsSubscriptionFilter:
Type: AWS::Logs::SubscriptionFilter
Properties:
FilterName: !Sub "${Env}-subscriptionfilter-${ResourceName}"
LogGroupName: !Ref LogGroupName
FilterPattern: !Ref FilterPattern
DestinationArn: !GetAtt Lambda.Arn
# ----- Permission to Lambda ----- #
PermissionToLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt Lambda.Arn
SourceArn: !Sub "arn:aws:logs:ap-northeast-1:${AccountId}:log-group:${LogGroupName}:*"
Principal: "logs.ap-northeast-1.amazonaws.com"
Action: "lambda:InvokeFunction"
PythonファイルをS3にアップロード
zip化したpythonコードをS3にアップロードしておきましょう。
詳細は、以下を参考にしてください。
スタックの作成
早速、CloudFormationを使用してリソースを作成していきます。
手順は、以下の通りで、リンクにも詳細がございます。
- CloudFormation画面を開きスタックの作成>新しいソースを使用(標準)を選択
- スタックに任意の名称を入力して、パラメータは環境に合わせて変更>次へを選択。※後述の手順に従う場合、デフォルト値でOK
- スタックオプションは特に変更せず、次へを選択
- テンプレートの準備完了>テンプレートファイルのアップロードを選択して、保存したymlを選択
- レビュー画面を確認して送信を選択
動作検証
では、エラーログを発生させて、メール通知されるか確かめていきましょう。
CloudWatchロググループでエラーログを発生させる
CloudWatchのロググループ画面を開きます。

ログストリームの作成を選択してtest-logstreamと入力し作成を選択します。

アクション>ログイベントの作成>ERRORと入力し作成を選択します。


これで、擬似的にERROR文字列の含むエラーログが出力されました。
サブスクリプションフィルターが検知して、SNS送信Lambdaを起動して、メール通知されているはずですので確認してみましょう。
まず、Lambdaのロググループを見てみると、確かに同時間帯に処理が動いていることがわかります。

メールの方も以下の通り届いていることが確認できました。

まとめ
今回は、CloudWatchLogsロググループのサブスクリプションフィルターとLambda、SNSを用いて、ログ検知と通知の仕組みをCloudFormationで作成しました。
実際の実装時には、Lambda側で詳細なフィルタリングをして適切に通知対象を絞るなど運用を考慮した作り込みも必要になるかと思います。
ロググループを使用しているため、使用したいシステムのログ出力先ををCloudWatchLogsにすることで今回の仕組みを簡単に使用できる点の手軽さが良いと思います、機会があれば使用してみます!
最後までご覧いただきありがとうございました。
コメント