こんにちは!
SJC共同開発推進室の坂根です。
システム運用をしていると、必ず直面するのが「ログ監視」の難しさですよね。
「とりあえずエラーを全部通知するようにしたけれど、通知が多すぎて結局スルーしてしまっている……」なんてことも。
本来、アラートは「今すぐ対応が必要な異常」を知らせるためのもの。
通知が埋もれてしまっては、せっかくの監視も意味をなしません。
そこで今回は、AWS CloudWatch・Lambda・SNS を活用して、『大量のログから重要なエラーだけを抽出して、メール通知する』という、実運用で役立つシンプルな仕組みを構築する方法をご紹介します。
ログ監視系ソリューション・サービスには、「Fluentd、Datadog、Prometheus」といった代表的なサービスもありますが、今回はシンプルに AWS サービスだけを使ってログ集約・自動通知する仕組みを構築してみます。
目次を見て「やることが多くて大変そう……」と思うかもしれませんが、複雑な実装や難しいコードを書く場面はほとんどありません。
参考までに、私自身がこの記事用に一通り検証したところ、構築から動作確認まで含めても1時間かからずに完了しました。
以降の手順を追いながら進めていけば、一通りの構成をそのまま試せる内容になっています。
全体構成
前提
本記事では、以下の状態を前提とします。
-
EC2(Amazon Linux 2023)が起動済み
-
アプリケーションログがテキスト形式で出力されている
-
EC2 に SSH ログインできる
※ EC2 の構築手順やアプリの実装は扱いません。
構成図
今回構築する流れは以下の通りです。
-
rsyslog でアプリログからエラーログを抽出
-
CloudWatch Agent でエラーログを CloudWatch Logs に送信
-
CloudWatch Logs のログを Lambda に連携
-
Lambda から SNS 経由でメール通知

1. rsyslog でログの絞り込み
まず rsyslog を使って、アプリケーションログからエラーログだけを別ファイルに切り出すように設定します。
「なぜ CloudWatch に送る前に rsyslog で絞るのか?」と疑問に思う方もいると思いますが、これは「CloudWatch への転送コスト削減と、ストレージ容量の節約のため」になります。
1-1. rsyslog のインストール
既にインストール済みであれば、3つ目のコマンドで rsyslog の起動を確認するだけで OK です。
|
1 2 3 |
sudo dnf install -y rsyslog #インストール sudo systemctl enable rsyslog #自動起動設定 sudo systemctl start rsyslog #起動 |
1-2. ログを抽出する設定
rsyslog の設定ファイルを作成します。
/etc/rsyslog.d/ の下に、[ファイル名].conf を作成し、以下の内容を記述します。
※今回は、sample_app というプログラム名でログを出力します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# /var/log/messages に出力されているものから、sample_app のアプリログを抽出 if $programname == 'sample_app' then { action(type="omfile" file="/var/log/sample_app/app.log") # ERROR を含む行 および # Syslog の Severity (重要度)が Error 以上のもの #(Emergency/Alert/Critical/Error) # cf. https://www.rfc-editor.org/rfc/rfc5424#section-6.2.1 # を抽出して別ファイルに出力する if ($msg contains "ERROR" or $syslogseverity <= 3) then { action(type="omfile" file="/var/log/sample_app/error.log") } stop } |
※本記事では、アプリケーションのログが /var/log/messages に出力されていることを前提としていますが、特定のログファイルから転記することも可能です。
その際は、rsyslog の imfile 機能を利用します。
1-3. 動作確認
rsyslog を再起動します。
|
1 |
sudo systemctl restart rsyslog |
アプリケーションからログを出力し、/var/log/sample_app/app.log と error.log が作成・更新されていれば成功です。
※以下は sample_app のアプリとして、syslog にログを出力するサンプルです。
|
1 2 |
logger -t sample_app "INFO: application start" logger -t sample_app "ERROR: application error occurred" |
app.log には両方のログが出力され、error.log には “ERROR” を含むログのみが出力されていれば、rsyslog のログ振り分け設定は正常に動作しています。
2. CloudWatch Agent で、サーバーからログを転送
次に、抽出したエラーログを CloudWatch Logs に転送します。
2-1. CloudWatch Agent のインストール
AWS 公式ドキュメントを参考に、CloudWatch Agent をインストールします。
※公式ドキュメントでは、yum を使用していますが、今回は Amazon Linux 2023 を使用しているため、dnf を使用しています。
|
1 |
sudo dnf install amazon-cloudwatch-agent |
2-2. CloudWatch Agent 設定ファイルの作成
CloudWatch Agent の設定ファイルを作成します。
ここでは、/var/log/sample_app/error.log を CloudWatch Logs に送信するよう設定します。
設定ファイルは JSON 形式で作成し、EC2 インスタンス上の任意の場所に配置できます。
今回は、CloudWatch Agent の標準的な配置先である以下のパスに作成します。
/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
以下は、本記事で使用する設定例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "logs": { "logs_collected": { "files": { "collect_list": [ { "file_path": "/var/log/sample_app/error.log", "log_group_name": "/sample_app/error", "log_stream_name": "SAMPLE EC2", "timezone": "Local" } ] } } } } |
各設定項目の概要
- file_path:転送対象のログファイルのパス
- log_group_name:CloudWatch Logs 上で作成されるロググループ名(例:アプリケーション名/ログ種別)
- log_stream_name:ログストリーム名(例:EC2 インスタンス ID)
- timezone:ログのタイムゾーン(”Local” を指定すると、EC2 のローカルタイムゾーンで時刻が解釈されます。)
2-3. CloudWatch Agent の起動
下記で CloudWatch Agent を設定し起動します。
|
1 2 3 4 5 |
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ -a fetch-config \ -m ec2 \ -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json \ -s |
各オプションの概要
- -a fetch-config:実行するアクション。 fetch-config は設定を読み込んで適用するアクション。
- -m ec2:実行環境の指定。このエージェントが EC2 上で動いていることを明示。
- -c file::読み込む設定ファイルのパスを指定。「file:パス」の形式。
- -s:エージェントを起動。
必要に応じて、以下のコマンドでエージェントの状態を確認できます。
2-4. CloudWatch Logs への転送確認
それでは、以下のコマンドを実行して、転送を確認してみましょう!
CloudWatch Logs のロググループを確認し、エラーログが転送されていれば成功です。

3. SNS トピック作成とメール通知設定
CloudWatch Logs から検知したログをメール通知するため、Amazon SNS(Simple Notification Service)を利用します。
3-1. SNS トピックの作成
SNS コンソールを開き、通知を送信するためのトピックを作成します。
- トピック名:任意(用途が分かる名前にしておくと運用しやすくなります)
- トピックタイプ:Standard(通常のログ通知用途であれば Standard で問題ありません)
作成後の設定例は以下の通りです。

3-2. メールサブスクリプションの登録
作成したトピックに対して、通知を受け取るメールアドレスを登録します。
トピックの詳細画面から「サブスクリプションの作成」を選択し、プロトコルに Eメール を指定して、エンドポイントには通知を受け取りたいメールアドレスを入力します。

サブスクリプションを作成すると、指定したメールアドレス宛に確認メールが送信されます。
3-3. サブスクリプション登録メールに関する注意点
登録後、確認メールが送信されます。
メール内の「Confirm subscription」をクリックすると、通知が受け取れるようになりますので、お忘れなく!

うまく登録できると、サブスクリプションのステータスが「確認済み」になります。
4. Lambda 関数作成
CloudWatch Logs からの通知を受け取り、SNS 経由でメールを送信する Lambda 関数を作成します。
4-1. Lambda 関数の作成
まずは Lambda 関数を作ります。Lambda コンソールから「関数の作成」を選んで、一から作るだけです。
名前をつけてランタイムを選ぶと、もうベースは完成となります。ほかの設定は特に変更不要です。
ランタイムはサンプルコードの都合で Python 系の最新を選択してください。例では Python 3.14 を選択しています。

4-2. 環境変数の設定
関数内で先ほど作成した SNS トピックの ARN を使用するのですが、コードに直書きする代わりに、環境変数として設定します。
ARN をコードに直書きしてしまうと、あとから修正するたびにコードを触ることになります。
環境変数にしておけば、設定だけ変更すればいいですし、コードの再利用もしやすいです。
Lambda の設定画面から環境変数に SNS_TOPIC_ARN を追加して、作成した SNS トピックの ARN を入れます。
※SNS トピックの ARN は、手順3-1で登録した SNS トピックのページでコピー可能です。


4-3. Lambda 関数コードの設定
作成した Lambda 関数を開き、lambda_function.py に以下のコードをコピーして貼り付けます。
コピーができたら、Deploy ボタンを押してデプロイしましょう。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import base64 import json import zlib import os import boto3 def lambda_handler(event, context): # CloudWatch Logs から送られてきたイベントをデコード data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS) data_json = json.loads(data) # ログストリーム名とメッセージを組み合わせて通知 log_stream = data_json.get("logStream", " # SNS クライアント生成 sns = boto3.client('sns') # logEvents には複数ログが含まれる可能性があるため全件処理する for log_event in data_json["logEvents"]: # 通知メッセージ作成 message = (f"LogStream: {log_stream}\n"f"{log_event['message']}") try: sns.publish( TopicArn=os.environ['SNS_TOPIC_ARN'], Message=message ) except Exception as e: print("SNSへの送信に失敗しました:", e) |
4-4. 実行ロール(IAM)の確認
次に、Lambda の実行ロールを確認します。
Lambda は「権限がないことは何もできない」サービスなので、実行ロールへ必要なポリシーを付与する必要があります。
下記で開いた IAM コンソールの編集画面で、SNS への Publish 権限を付与してください。

※④では以下の JSON を入力しました。(Resource は作成した SNS の ARN です。)
|
1 2 3 4 5 6 7 8 9 10 11 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "sns:Publish", "Resource": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:sample-topic" } ] } |
5. CloudWatch Logs サブスクリプションフィルター作成
ここからは、ログの中でも特定の文字列を含むログだけを通知する設定を行います。
5-1. サブスクリプションフィルター作成手順
CloudWatch Logs の対象ロググループを開き、「サブスクリプションフィルター」から「作成」>「Lambda サブスクリプションフィルターを作成」を選択します。
送信先には、先ほど作成した Lambda 関数を指定します。

5-2. フィルター文字列の設定
ここでは、“Internal Server Error” を入力します。

すると、ログメッセージの中に Internal Server Error を含むものだけが Lambda に送られるようになります。
フィルターを設定することで、サーバーから CloudWatch へ転送されたエラーログの中でも、さらに重要なもののみ通知することができるようになります!
ポイント
ここで指定する文字列は、アプリケーションの特性に合わせて調整してください。
例えば、特定の API エラー(”API_ERROR_500″など)や、致命的な例外(”CRITICAL”)などを指定することで、「深夜対応が必要となる重要ログ」だけに絞り込むことができます。また、フィルタ指定時は以下の点に注意してください。
- フィルタ文字列は大文字・小文字を区別する
- (例:ERROR と error は別扱い)
- 空白を含む文字列は、「” “」で囲む
- (例:”Internal Server Error”)
- 単純に空白で区切ると AND 条件になる
- (例:ERROR CRITICAL)
- OR 条件にする場合は、各語の先頭に「?」を付ける
- (例:?ERROR ?CRITICAL)
5-3. 登録
他の設定項目は初期状態のままでも構わないので、画面下部の「ストリーミングを開始」よりサブスクリプションフィルターを作成します。
作成されると、対象ロググループに、作成したフィルタが確認できると思います。

6. 動作確認
これで一通りの設定が完了しました。
少し手順が多かったですが、ここまでお疲れさまでした。
それでは、実際に動作確認をしてみましょう。
6-1. テスト用ログの出力
サーバー上で以下のコマンドを実行してみます。
|
1 2 3 4 5 6 7 8 |
# app.log には出力されるが、error.log には出力されない logger -t sample_app "INFO: application start" # error.log に出力され、CloudWatch には転送されるが、メール通知はされない logger -t sample_app "ERROR: application error occurred" # error.log に出力され、CloudWatch 経由で Lambda が処理し、メール通知までされる logger -t sample_app "ERROR: Internal Server Error" |
6-2. CloudWatch Logs での確認
エラーログだけが CloudWatch Logs に出力されていることを確認します。

Lambda 実行時には、/aws/lambda/<関数名> というロググループが自動作成され、実行ログやエラー内容が出力されます。
今回のサンプルコードでエラーが発生した場合も、こちらに記録されます。
保持期間は 2週間程度 に設定変更しておくとよいです。
6-3. SNS メール通知の確認
登録したメールアドレスに、”Internal Server Error” が含まれるエラーログのみ、通知されるはずです!

まとめ
本記事では、 rsyslog・CloudWatch Logs・Lambda・SNS を組み合わせて、 重要なログのみをメール通知する構成を紹介しました。
ログをすべて通知してしまうと、やがて通知に慣れてしまい、本当に重要なエラーを見逃してしまう可能性があります。
そのため、今回のように
- サーバー側でログを整理し
- CloudWatch へ転送し
- フィルターで絞り込んだものをメール通知
という多段構成にすることで、実運用に耐えうる監視設計が実現できます。
今回の構成は、EC2 上のアプリケーションログを例にしていますが、ロググループやフィルター条件を変更すれば、さまざまなシステムに応用可能ですので、ぜひご自身の環境でも試してみてください。
参考記事
CloudWatch Logs を文字列検知してログ内容をメールを送信してみた サブスクリプションフィルター版(吉井 亮/DevelopersIO)
エコモットでは一緒にモノづくりをしていく仲間を随時募集しています。弊社に少しでも興味がある方はぜひ下記の採用ページをご覧ください!





