AWS CloudWatch Logsで例外のログを取得する


現在勤めている企業ではDjangoで開発したアプリケーションをDocker Composeを利用して運用しています。

Dockerのログは以下のコマンドで確認することができます。

# appコンテナのログエントリーを表示し続ける(-fオプションでtail)
$ docker-compose logs -f app

# appコンテナの直近の500件のログエントリーを表示する
$ docker-compose logs --tail=500 app

しかし、運用していると、様々なトラブルがあって、トラブルの都度コンテナを実行しているサーバーにログインして、コンテナのログを確認するのが面倒臭い。

また、コンテナを破棄すると、今までのログも破棄してしまうため、運用環境では外部のログ保存ソフトウェアにログを送って保存します。Docker Composeではlog driverを利用することで、fluentdやAWS、GCPにログの送信・保存を行うことができます。

現在アプリケーションはAWSで運用しているので、以下の1〜3を行い、AWS CloudWatch Logsにログを送ることにします。

1. EC2インスタンスにIAMロールを設定
2. CloudWatch Logsでロググループを作成
3. docker-compose.ymlを修正

1. EC2インスタンスにIAMロールを設定

コンテナを実行しているEC2インスタンスに以下のIAMロールを作成し設定する。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

参考:
Amazon CloudWatch Logs ロギング・ドライバ

2. CloudWatch Logsでロググループを作成

AWSのコンソールでCloudWatchにアクセスし、ログ-->ロググループで「アクション」ボタンを押下します。そして、表示したダイアログにロググループ名を入力し、「ロググループの作成」ボタンを押下し作成します。

3. docker-compose.ymlを修正

以下の設定をdocker-compose.ymlに追記します。

docker-compose.yml
version: '3'
services:
  app:
    ・・・(中略)・・・
    logging:
      driver: "awslogs"
      options:
        awslogs-region: "us-east-2" # オハイオリージョン
        awslogs-group: "/sample-app/dev/"
        awslogs-create-group: "true" # グループがない場合は作成
        awslogs-stream: "web"

以下のようにCloudWatch logsに保存できるようになりました。

ログをCloudWatch logsに保存できるようになったのは良いですが、例外が発生した場合、ログが以下のように1行ずつ分割してしまい非常に見づらくなってしまいました。例外が発生したスタックトレースも1行ずつ分割しないで1行で保存するようにします。

docker-compose.ymlに「awslogs-multiline-pattern」を追加します。またログを構造化してCloudWatch上で検索しやすくするために、django-structlogというモジュールをインストールします。django-structlogについては参考をご参照下さい。

docker-compose.yml
version: '3'
services:
  app:
    ・・・(中略)・・・
    logging:
      driver: "awslogs"
      options:
        awslogs-region: "us-east-2" # オハイオリージョン
        awslogs-group: "/sample-app/dev/"
        awslogs-create-group: "true" # グループがない場合は作成
        awslogs-stream: "web"
        awslogs-multiline-pattern: '^{"request_id":'

以下のように例外も1行のログとしてCloudWatch Logsに保存できるようになりました。

参考:
Djangoでロギング(logging)を行う方法