[アップデート] CloudWatch エージェントによる CloudWatch Logs へのログ出力でフィルタリングとログ保持期間の設定ができるようになりました

CloudWatch エージェント設定ファイルで指定できるフィールドが増えました

コンバンハ、千葉(幸)です。

CloudWatch エージェントによる CloudWatch Logs へのログ出力において、ふたつの新機能が使えるようになりました。

ログフィルター式と、ログ保持期間の指定です。

前者は必要なログのみ CloudWatch Logs に出力することを可能とし、見通しの良さやコスト削減に寄与します。

後者はログ保持期間を指定したい場合の追加設定が不要になり、特に動的にロググループが作成される場合の管理の手間を減らしてくれます。

助かります。

何が変わったのか

CloudWatch エージェント設定ファイルのlogsセクションに以下フィールドを追加できるようになりました。

  • filters
  • retention_in_days

例えば以下のように書けます。

CloudWatchエージェント設定ファイルの例

{
	"logs": {
		"logs_collected": {
			"files": {
				"collect_list": [
					{
						"file_path": "/var/log/test",
						"log_group_name": "/var/log/test",
						"log_stream_name": "{instance_id}",
						"filters": [
							{
								"type": "exclude",
								"expression": "Firefox"
							},
							{
								"type": "include",
								"expression": "P(UT|OST)"
							}
						],
						"retention_in_days": 30
					}
				]
			}
		}
	}
}

いずれのフィールドも 2022/2/26 時点で最新の CloudWatch エージェントでのみ使用できます。S3 リンクからダウンロードできるものは最新バージョンになっていますが、yum でインストールできるものは少し古いバージョンになっているようなので注意してください。

各フィールドの詳細は以下ドキュメントから確認できます。

CloudWatch Logs に出力するログのフィルタリング

これまで CloudWatch エージェントは設定ファイルで指定されたパスのログをすべて CloudWatch Logs へ出力していました。特定のキーワードにマッチするもののみ CloudWatch Logs で取り扱いたいという要件が有った場合、全量が送られることはあまり嬉しくありません。

  • ノイズが増えてしまう
  • CloudWatch Logs への取り込み、ログの保管で不要な料金が発生する

今回のアップデートにより正規表現を用いてincludeもしくはexcludeのタイプでフィルターを作成できるようになりました。

例えば以下のように指定したとします。これはドキュメントから引用しました。

						"filters": [
							{
								"type": "exclude",
								"expression": "Firefox"
							},
							{
								"type": "include",
								"expression": "P(UT|OST)"
							}
						],

上記の指定をした場合、以下のように機能します。

  • Firefoxの文字列を含むログイベントはすべて破棄する
  • PUTもしくはPOSTの文字列を含むログイベントを CloudWatch Logs に出力する
    • 合致しないログイベントはすべて破棄する

ここで言っている「破棄」とは CloudWatch Logs への出力対象から外すことを指しており、ローカルのログファイルに影響はありません。

expressionで指定できる正規表現パターンは以下に準拠しています。

filters によるフィルタリングにおける注意点

  • 正規表現の評価に時間がかかる書き方は避けてください
  • フィルターは上から順番に評価されます
    • パフォーマンスの向上のために、複数のフィルターで評価されるログエントリを少なくなるよう順序を決定してください
      • 広い範囲でexcludeするフィルターを優先度高くする

CloudWatch Logs ロググループのログ保持期間の指定

CloudWatch Logs ロググループではログの保持期間という概念があります。指定した保持期間を経過したログイベントは順次削除されていきます。デフォルトでは保持期間が無期限となっており、ログが溜まり続けていきます。

ログの保存容量に応じて料金が発生するため、保持期間を明示的に指定するケースは多いです。

これまで CloudWatch エージェントの設定と CloudWatch Logs のログ保持期間は独立していたので、管理を工夫してあげる必要がありました。

  • 事前にロググループの作成/保持期間の設定を済ませた上でエージェント設定ファイルで出力先に指定する
  • 一度 CloudWatch エージェントからログ出力を行った上で、作成されたロググループの保持期間を後から変更する など

CloudWatch エージェント設定ファイルでは動的にロググループ名を指定できる(例えばインスタンス名にするなど)ため、新規のロググループがどんどん作成されるような構成の場合、管理は複雑になります。

今回のアップデートにより、CloudWatch エージェント設定ファイルでログ保持期間を定義できるようになり、自動で設定変更できるようになりました。

保持期間として指定できる値は 1、3、5、7、14、30、60、90、120、150、180、365、400、545、731(2年間)、1827(5年間)、3653(10年間)のいずれかです。

指定しない場合、従来と同様「無期限」となります。

retention_in_days によるログ保持期間の指定の注意点

  • CloudWatch エージェントが使用する IAM ロールや IAM ユーザーが以下の権限を有している必要があります
    • logs:PutRetentionPolicy
  • 既存のロググループにretention_in_daysを指定した場合、設定が上書きされます
    • 例えばここで3を指定した場合、既存のログは 3 日分を残して削除されます
  • エージェント設定ファイルで同一ロググループの複数のログストリームを定義する場合……
    • 一箇所でlogs:PutRetentionPolicyを定義すれば事足ります
    • ログストリーム間で異なるlogs:PutRetentionPolicyを定義するとエラーになります

最後について補足すると、以下のような書き方をするとエラーになるよ、ということです。

......
"collect_list": [
					{
						"file_path": "/var/log/testA",
						"log_group_name": "/var/log/test",
						"log_stream_name": "streamA",
						"retention_in_days": 30
					},
					{
						"file_path": "/var/log/testB",
						"log_group_name": "/var/log/test",
						"log_stream_name": "streamB",
						"retention_in_days": 90
					},					
......

上記の例では二つのコレクションで同一のロググループ/var/log/testを指定しています。両者で異なるretention_in_daysの値を指定しているため、正しい書き方ではありません。以下のいずれかにする必要があります。

  • 一方のコレクションでのみretention_in_daysを定義する
  • 両者のコレクションで同一のretention_in_daysの値を定義する

保持期間を両者で変えたい、という場合はロググループごと分けてあげる必要があります。

やってみた

冒頭で載せたエージェント設定ファイルを適用し、ログ出力の結果を確認してみます。エージェントのインストールから一連の流れで試していきます。

  • CloudWatch エージェントのインストール
  • CloudWatch エージェント設定ファイルの適用
  • CloudWatch エージェントによるログ出力のテスト

CloudWatch エージェントのインストール、設定ファイルの変更は Systems Manager を利用して行います。以下を参考にしています。

0. EC2 インスタンスのセットアップ

Systems Manager が利用可能なことが前提となっています。今回は以下でセットアップしました。

  • amzn2-ami-kernel-5.10-hvm-2.0.20220207.1-x86_64-gp2 からローンチ
    • SSMエージェントがインストール済み
  • パブリックサブネットに配置しパブリック IP を割り当て
  • IAM ロールにAmazonSSMManagedInstanceCoreを割り当て

インスタンスが利用する IAM ロールには以下も追加でアタッチしています。

  • CloudWatchAgentServerPolicy
    • CloudWatch Logs への出力、パラメータストアからの読み取り
  • カスタムポリシー

カスタムポリシーの内訳は以下で、先述の通りログ保持期間を変更するために必要となります。

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

1. CloudWatch エージェントのインストール

Systems Manager ランコマンドで SSM ドキュメントAWS-ConfigureAWSPackageを実行します。

指定するパラメータは以下で、NameVersionを明示的に指定しています。

パラメータ
Action Install
Installation Type Uninstall and reinstall
Name AmazonCloudWatchAgent
Version latest
Additional Arguments {} ※デフォルト

実行することでインストールが実行されます。出力を確認すると以下のバージョンの CloudWatch エージェントがインストールされていました。

Initiating arn:aws:ssm:::package/AmazonCloudWatchAgent 1.247350.0b251780 install
Plugin aws:runShellScript ResultStatus Success
install output: Running sh install.sh
create group cwagent, result: 0
create user cwagent, result: 0

Successfully installed arn:aws:ssm:::package/AmazonCloudWatchAgent 1.247350.0b251780

2. CloudWatch エージェント設定ファイルの適用

事前準備としてエージェント設定ファイルをパラメータストアに格納しておきます。現状の IAM ポリシーだと接頭辞AmazonCloudWatch-を持つパラメータのみ読み取りができるため、今回はAmazonCloudWatch-agent-configという名称で作成します。

AWS_Systems_Manager_-_Parameter_Store_agentconfig

格納した値は冒頭で記載した例と同じく以下です。

{
	"logs": {
		"logs_collected": {
			"files": {
				"collect_list": [
					{
						"file_path": "/var/log/test",
						"log_group_name": "/var/log/test",
						"log_stream_name": "{instance_id}",
						"filters": [
							{
								"type": "exclude",
								"expression": "Firefox"
							},
							{
								"type": "include",
								"expression": "P(UT|OST)"
							}
						],
						"retention_in_days": 30
					}
				]
			}
		}
	}
}

この状態で Systems Manager ランコマンドで SSM ドキュメントAmazonCloudWatch-ManageAgentを実行します。

パラメータ
Action configure
Mode ec2
Optional Configuration Source ssm
Optional Configuration Location AmazonCloudWatch-agent-config
Optional Open Telemetry Collector Configuration Source ssm
Optional Open Telemetry Collector Configuration Location (ブランク)
Optional Restart yes

Optional Configuration Locationとしてパラメータの名称を指定した以外はデフォルトのままです。

実行することで設定ファイルが適用されます。

出力を確認すると以下のようにログが出ていました。

2022/02/26 07:52:38 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/ssm_AmazonCloudWatch-agent-config.tmp ...
Created symlink from /etc/systemd/system/multi-user.target.wants/amazon-cloudwatch-agent.service to /etc/systemd/system/amazon-cloudwatch-agent.service.
Redirecting to /bin/systemctl restart amazon-cloudwatch-agent.service

念のため設定確認

設定が終わった状態で Systems Manager セッションマネージャーでインスタンスに接続し、エージェントの設定内容を確認してみます。

/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.tomlを参照します。

sh-4.2$ cat /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml
[agent]
  collection_jitter = "0s"
  debug = false
  flush_interval = "1s"
  flush_jitter = "0s"
  hostname = ""
  interval = "60s"
  logfile = "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log"
  logtarget = "lumberjack"
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  omit_hostname = false
  precision = ""
  quiet = false
  round_interval = false

[inputs]

  [[inputs.logfile]]
    destination = "cloudwatchlogs"
    file_state_folder = "/opt/aws/amazon-cloudwatch-agent/logs/state"

    [[inputs.logfile.file_config]]
      file_path = "/var/log/test"
      from_beginning = true
      log_group_name = "/var/log/test"
      log_stream_name = "i-04c7d35135e594a07"
      pipe = false
      retention_in_days = 30

      [[inputs.logfile.file_config.filters]]
        expression = "Firefox"
        type = "exclude"

      [[inputs.logfile.file_config.filters]]
        expression = "P(UT|OST)"
        type = "include"
    [inputs.logfile.tags]
      metricPath = "logs"

[outputs]

  [[outputs.cloudwatchlogs]]
    force_flush_interval = "5s"
    log_stream_name = "i-04c7d35135e594a07"
    region = "ap-northeast-1"
    tagexclude = ["metricPath"]
    [outputs.cloudwatchlogs.tagpass]
      metricPath = ["logs"]

ログの保持期間、フィルター設定が意図した通りになっています。

3. CloudWatch エージェントによるログ出力のテスト

インスタンスに接続後/var/log/testにいくつかのログを出力し、挙動を確認します。

出力するログは以下です。

Firefox PUT
Firefox POST
Firefox GET
Chrome PUT
Chrome POST
Chrome GET
PUT
POST
GET

フィルターの条件をおさらいすると以下です。

  • Firefoxの文字列を含むログイベントはすべて破棄する
  • PUTもしくはPOSTの文字列を含むログイベントを CloudWatch Logs に出力する
    • 合致しないログイベントはすべて破棄する

root にスイッチしてから以下を実行しました。

[root@ip-192-168-0-96 ~]# cat << _EOF_ >> /var/log/test
> Firefox PUT
> Firefox POST
> Firefox GET
> Chrome PUT
> Chrome POST
> Chrome GET
> PUT
> POST
> GET
> _EOF_
[root@ip-192-168-0-96 ~]# cat /var/log/test
Firefox PUT
Firefox POST
Firefox GET
Chrome PUT
Chrome POST
Chrome GET
PUT
POST
GET

CloudWatch Logs ロググループを確認すると、指定した通りの保持期間が設定されています。

CloudWatch_Management_Console_Loggroup2

ログストリームに出力されたログイベントを確認すると、意図した通りの結果のみが出力されています。

CloudWatch_Management_Console_stream

ログのフィルタリング、保持期間の設定の挙動が確認できました!

終わりに

CloudWatch エージェント設定ファイルでフィルター式と保持期間の定義ができるようになった、というアップデートでした。

東京リージョンでは CloudWatch Logs のログ取り込み、保存に以下の料金がかかります。

項目 料金
収集 (データの取り込み) 0.76USD/GB
保存 (アーカイブ) 0.033USD/GB

フィルターによって取り込む量を減らしたり、保持期間を短く(することを漏れにくく)して保存量を減らしたりとコスト面でも嬉しいアップデートかと思います。

現時点の最新バージョンでしか使用できないという制約はありますが、エージェントの最新化も兼ねて機能の活用を検討してみてはいかがでしょうか。

以上、 チバユキ (@batchicchi) がお送りしました。