たそらぼ

日頃思ったこととかメモとか。

ロググループから特定ログを抽出するフィルターのパターンについて考える

複数種のログが混ざって格納されるロググループにサブスクリプションフィルタを設定して、特定種類のログのみ取り出したいのですが、パースとフィルターをする方法に癖があったのでまとめました。

やりたいこと

docs.aws.amazon.com

CloudWatch Logsでは、収集したログに対してフィルターを使うことで、様々な機能を実現することができます。

  • ログ内の文字をひっかけてカスタムメトリクスに変換する
  • サブスクリプションフィルタでkinesisなどほかのサービスに転送する

今回は、あるロググループに複数種類のログがまとめて格納されているとき、特定のログだけCloudWatch Logs外に転送するユースケースを考えています。
特に、ApacheログやSecureログなどは普通スペース区切りで格納されると思いますが、ペース区切りのログを対象としたパターンの書き方に癖があるので、上手くひっかける方法を調査しました。

Apacheログをパースする

角括弧 [] や 2 個の二重引用符 ("") が入っているログ

例えば以下のようなログです。

127.0.0.1 - frank [10/Oct/2000:13:25:15 -0700] "GET /apache_pb.gif HTTP/1.0" 200 1534

いわゆるApacheアクセスログですが、こちらは公式ガイドに例があり、標準のフォーマットであれば簡単に検知できるようです。
docs.aws.amazon.com


この例では、"[ip, user, username, timestamp, request, status_code, bytes]"という風に、ログに対してフィールド名を自分でつけたサブスクリプションフィルターを作成しています。

f:id:tasotasoso:20200524160416p:plain:w500
Apacheアクセスログの例1

ここで気になるのが、

  • [10/Oct/2000:13:25:15 -0700]
  • "GET /apache_pb.gif HTTP/1.0"

です。一見非常に簡単にパースできるように見えます。

ドキュメントを見ると、

メトリクスフィルターを使用してスペース区切りログイベントから値を取得できます。角括弧 [] または 2 個の二重引用符 ("") で囲まれた文字は単一のフィールドとして扱われます。

基本的に、前方から空白区切りでパースされるような挙動をするので、フォーマットとしてまとめて認識したい場合は、あらかじめログ内で角括弧 [] または 2 個の二重引用符 ("") で囲まれている必要があります。

角括弧 [] や 2 個の二重引用符 ("")が入っていないログ

例えば以下のようなログです。

127.0.0.1 - frank 10/Oct/2000:13:25:15 -0700 GET /apache_pb.gif HTTP/1.0 200 1534

さっきのログとは異なり、角括弧 [] や 2 個の二重引用符 ("")が入っていません。
このログに対して先ほどのようにパターンを設定すると以下のような結果になります。

f:id:tasotasoso:20200524163026p:plain:w500
パースに失敗した例
$requestに時差が来ていて、明らかにずれています。

上手く認識させるためには、フィールド名を増やす必要があります。

f:id:tasotasoso:20200524163211p:plain:w500
パースに成功した例

ちなみに、[ip, user, username, timestamp, timestamp2, ... ,request, status_code, bytes] のように、フィールド数が不明な場合に省略符号("...")を入れることができますが、まとめて解釈してくれるというよりは、適当なフィールド名を付けて認識してくれるようです。

f:id:tasotasoso:20200524164045p:plain:w500
パースに失敗した例2

このように、フィールド名を増やすことでパースをすること自体は可能ですが、ログフォーマットの定義に従ったフィールド定義は場合によってはできないことが分かります。

Syslog ログをパースする

つづいて、別の例としてSecureログをパースしてみます。
設定にもよると思いますが、ログの例としては以下のようなものを考えるとよさそうです。

#https://www.ipa.go.jp/security/awareness/administrator/secure-web/chap8/8_log-2.html

Feb 13 21:47:04 sol8 rsh[2429]: connection from bad port
Feb 13 21:47:04 sol8 rsh[2429]: connection from bad port
Feb 13 21:47:05 sol8 telnetd[2424]: ttloop: peer died: Bad file number
Feb 13 21:47:05 sol8 telnetd[2424]: ttloop: peer died: Bad file number
Feb 13 21:47:05 sol8 bsd-gw[2417]: Error reading from connection: Bad file number
Feb 13 21:47:05 sol8 bsd-gw[2417]: Error reading from connection: Bad file number
Feb 13 21:47:06 sol8 sendmail[2420]: NOQUEUE: Null connection from [192.168.200.10]
Feb 13 21:47:06 sol8 sendmail[2420]: NOQUEUE: Null connection from [192.168.200.10]

secureログはフィールド数も少ないので、フィールド名だけだと違うログもひっかける可能性が高そうです。
そこで、syslogtagやtimestampは形式が決まっているので、条件を入れていきたいと思います。
フィルターはこんな感じになります。

[month, day, time = "*:*:*",  hostname,syslogtag = "*[*]:", msg]

f:id:tasotasoso:20200524170731p:plain:w500
syslogのフィルターの結果

なお、条件の書き方は、以下に注意すると大体書けました。

  • 文字列は""で囲む
  • ""で囲っても、元のログにスペースが入っていると1フィールドとしては認識しない

Apacheログが混ざったロググループから、Syslogだけ抽出する

ここまでで条件付きでログをフィルターする方法が分かったので、最後に、Apacheログや、ほかの文字列と混ざったログからsecureログ"だけ"抽出するフィルターを確認します。
ログは以下を例として使ってみます。
2行目にApacheログ、4行名にsecureログっぽいけどなんか違う変な文字列をいれてあります。

Feb 13 21:47:04 sol8 rsh[2429]: connection from bad port
127.0.0.1 - frank 10/Oct/2000:13:25:15 -0700 GET /apache_pb.gif HTTP/1.0 200 1534
Feb 13 21:47:04 sol8 rsh[2429]: connection from bad port
Feb 13 21:47:05 sol8 telnetd[2424]: ttloop: peer died: Bad file number
AAA 99 0000000 sol8 xxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyy
Feb 13 21:47:05 sol8 telnetd[2424]: ttloop: peer died: Bad file number
Feb 13 21:47:05 sol8 bsd-gw[2417]: Error reading from connection: Bad file number
Feb 13 21:47:05 sol8 bsd-gw[2417]: Error reading from connection: Bad file number
Feb 13 21:47:06 sol8 sendmail[2420]: NOQUEUE: Null connection from [192.168.200.10]
Feb 13 21:47:06 sol8 sendmail[2420]: NOQUEUE: Null connection from [192.168.200.10]

※ふつうは流石にログ種別ごとにlogStreamが分かれていると思いますが、検証しやすくするため一緒くたにしています。付けられたサブスクリプションフィルターから見ると、このように見えるはずです。


フィルターはSecureログの時と同じです。

[month, day, time = "*:*:*",  hostname,syslogtag = "*[*]:", msg]

f:id:tasotasoso:20200524171442p:plain:w500
Secureログだけフィルターした結果

ちゃんとSecureログだけヒットしていることがわかります。

感想

条件とフィールド名の数を組み合わせることでSecureログを分離することができました。ただ、正規表現に比べるとだいぶざっくりしているので、フィルターした後に違うログが混ざってしまう可能性がもありそうでした。

今回は同じロググループ内に異なるログ種別のログが混ざる想定でしたが、そもそも最初から異なるログ種別は異なるロググループになるように設計するのがよいのかなぁと思います。

参考

IPAのsyslogの説明。Secureログの例はここから拝借しました。
www.ipa.go.jp

create-export-taskで特定のログストリームだけエクスポートしたい

create-export-task APIで特定のログストリームだけエクスポートする方法を調べました。
docs.aws.amazon.com

モチベーション

AWS CLIからcreate-export-task APIを叩くと、CloudWatch Logsの特定のログストリームのログ を取ることができる記事を書きました。
tasotasoso.hatenablog.com

ただ、システムの設計によっては、一つのロググループに全てのログを突っ込んで、ログストリーム名でログ種別を区別しているかもしれません。
そのため、ログストリーム単位で取る方法も知りたいなということで調べました。

f:id:tasotasoso:20200517224113p:plain:w400
やりたいこと

ログストリームごとに取る方法

--log-stream-name-prefix でログストリームのプレフィクスを指定できるので、これを使っていきます。
例として、my-log-streamという名前のログストリームを考えます。

ログストリーム名を指定する。

プレフィクスをエクスポート対象のログストリーム名に一致させることで可能です。

aws logs create-export-task (略) --log-stream-name-prefix my-log-stream

ログストリームのプレフィクスを指定する

--log-stream-name-prefix にエクスポート対象のログストリームに含まれるプレフィクスを指定することで可能です。

aws logs create-export-task (略) --log-stream-name-prefix my-log

ログストリームを後方一致させる

例えば、以下のような指定で"stream"がログストリーム名の最後にくるログストリームだけをエクスポートしたい場合です。

aws logs create-export-task (略) --log-stream-name-prefix *stream

こちらは、残念ながら無理そうでした。悲しい。


Create-Export-taskのAPIドキュメントをみると、

logStreamNamePrefix
Export only log streams that match the provided prefix. If you don't specify a value, no prefix filter is applied.

Type: String

Length Constraints: Minimum length of 1. Maximum length of 512.

Pattern: [^:*]*

Required: No
CreateExportTask - Amazon CloudWatch Logs

ということで、残念ながらワイルドカードは使用不可のようです。

cliで試してみると、以下のように怒られます。

An error occurred (InvalidParameterException) when calling the CreateExportTask operation: 1 validation error detected: Value '*stream' at 'logStreamNamePrefix' failed to satisfy constraint: Member must satisfy regular expression pattern: [^:*]*

また、.+などでなんとかならないかと試してみましたが、.や+なども普通の文字として取られるようでダメでした。(もし、やり方をご存知の方は教えてください。。。)

後方一致の代替策

describe-log-streams APIであるロググループ内にあるログストリームの一覧を取ることができます。
ログストリーム名を取得して、目的にあったログストリーム名ごとにcreate-export-taskを作るのが良さそうです。
docs.aws.amazon.com

ちなみに、返却されたjsonには、creationTimeやなどいろいろ情報が入っているので、使い道がありそうです。

#From https://docs.aws.amazon.com/cli/latest/reference/logs/describe-log-streams.html

{
    "logStreams": [
        {
            "creationTime": 1433189871774,
            "arn": "arn:aws:logs:us-west-2:0123456789012:log-group:my-logs:log-stream:20150531",
            "logStreamName": "20150531",
            "storedBytes": 0
        },
        {
            "creationTime": 1433189873898,
            "arn": "arn:aws:logs:us-west-2:0123456789012:log-group:my-logs:log-stream:20150601",
            "logStreamName": "20150601",
            "storedBytes": 0
        }
    ]
}

参考

github.com
今回のAPIとは違いますが、CloudWatch Logsのログをfluentdのpluginで読み出す際に、ログストリームはワイルドカードで指定できないというIssueです。共通の悩みのようです。

docs.aws.amazon.com
いろいろ実験して、不発に終わったcreate-export-taskがどうなってるのか心配だったので、taskの一覧を表示させるのに使えます。

FargateでCloudWatch Logsからログをエクスポートするアイデア

fargateからAWS CLIでcreate-export-task APIを叩き、CloudWatch Logsからログをエクスポートする方法を考えてみました。

モチベーション

CloudWatch Logsに格納されたログを取り出して、別のシステムに移したいような要件があると思います。
例えば、複数システムを運用している環境で、監査用システムにログを集約したいような時などです。

CloudWatch Logsからログを取り出す方法はいくつかあります。
例えば、

  1. サブスクリプションフィルタ
  2. エクスポート機能
  3. fluentdなどの3rd party製品

ただ、どの方法も意外と面倒臭く、こういう要件に当たっている方は結構苦労している印象があります。
具体的には、

  1. ロググループあたり1個までしか使えない(2020/5現在) ため、既に埋まっていると使えない。しかもデータ部分がbase64&gzip圧縮されるので、デコードしないといけない。
  2. エクスポート後、続けて処理をするようなワークフローを作るのに、Step Functionsなどでテクいことをしないといけない。わざわざEC2を立てて、lambdaで起動/停止するのもなんか違う気がする。
  3. AWS公式の読み出しpluginがない。

バッチ処理でいいので、なんとか簡単にできないかと思っていたところ、Fargateが思い当たり、検証してみることにしました。

構成

こんな感じのことを考えています。

f:id:tasotasoso:20200515084925p:plain:w400
Fargateからエクスポート

Docker Imageを作る

エクスポートタスクを作る用のDocker Imageを作ります。

以下の公式ドキュメントを参考に、fargateからcreate-export-task APICLIから叩く作戦です。
docs.aws.amazon.com

AWS CLIを使う分には、公式でイメージ(AWS CLI v2 Docker image | AWS Developer Blog)を公開してくれていますが、個人的にpythonのイメージで自作した方がやりやすかったので、そちらで行きます。

そうすると、以下のようなDockerfileになります。

FROM python:3.7-alpine                                                                                                                                    
RUN pip3 install awscli

CMD aws logs create-export-task --task-name "my-log-group-09-10-2015" --log-group-name "my-log-group" --from 1441490400000 --to 1441495000000 --destination "my-exported-logs" --destination-prefix "export-task-output"


なお、create-export-task APIの仕様は、下記のドキュメントにあります。注意点としては、エクスポートするログのタイムスタンプを、ミリ秒単位のUNIXタイムで書くのが厄介です。
docs.aws.amazon.com


今回は時間をdocker fileに決め打ちで書いていますが、dateコマンドで動的に取得して取れると良いです。なお、ミリ秒単位のUNIXタイムは下記で取得できます。

date +%s%3

fargateにはIAMロールで権限を設定する

create-export-task APIを実行するには、実行側に、CloudWatch LogsとS3のフルアクセスが必要なので、タスクロールとして使用するIAMロールにつけておきます。
後はpushしたDocker imageをfargateにpullしてタスクとして実行するだけです。

fargateを使うのが初めての方は、初心者向けのサイト等を参考に設定してみてください。
dev.classmethod.jp
docker imageをビルドして、ECRにpushしておくのも忘れずに。


スケジューリング

タスクはECSの機能で一貫して、cron方式でスケジューリングできるようです。この機能でスケジュールしておけば、簡単にバッチ処理化できそうです。
docs.aws.amazon.com
qiita.com

感想

この方法であれば、指定の時間になればFargateが勝手に起きてくれてエクスポートして落ちてくれるので、めっちゃ楽ですし、安く済みます。
Lambdaを駆使して頑張ったり、事故ったりする必要もなさげです。
EC2を立てる程ではないけど、Lambdaのカバー範囲より少し長めの時間がかかる処理って、とても大変だなぁと痛感しました。Fargateがあってくれて本当によかったです。

CloudWatch Logsのサブスクリプションフィルタの上限数問題と対応策

記事執筆現在、CloudWatch Logsのサブスクリプションフィルタ数には上限がありますが、どこに書いてあるか分かりにくかったのでメモです。

サブスクリプションフィルタについて

CloudWatch Logsに流れ込んでくるログデータを、フィルタリングして、リアルタイムに別のAWSサービスに転送することができる機能です。ロググループ単位で作成することができ、すごく便利な機能なのですが、一度にロググループにつけることができる数に上限があります。

1 ロググループあたり 1つまで

CloudWatch Logsの制限については、公式ドキュメントの以下に記載があります。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html

1 ロググループあたり 1。このクォータは変更できません。

まじか、もっとつけさせてくれ。。。

対応策

サブスクリプションフィルタの転送先をKinesis streamにして、複数コンシューマで読み取ることができます。
docs.aws.amazon.com

f:id:tasotasoso:20200426072942p:plain:w500
kinesis streamをはさむ構成

データ部分は圧縮される

サブスクリプションフィルタで送信されるデータは、データ容量削減のため圧縮されます。Kinesis streamに流す場合、Kinesis レコードの Data 属性が、Base64エンコードされ、かつ、gzip 形式で圧縮された状態になっています。そのため、ログを使う場合は、Base64デコードして、さらに、gzip解凍する必要があります。

  • Node.jsで解凍する方法

ja.coder.work

bbh.bz

読み取りの競合が激しい場合

kinesis streamはデータの読み取りスループットが最大2MB/secですが、この構成だとデータの読み取り時に競合が起きてしまうため、頻繁に読み出しを行う場合は、拡張ファンアウトを作成すると良さそうです。
docs.aws.amazon.com

価格面

Kinesis streamはそこそこお金がかかります。1シャード置いておくだけでも1500~2000円/月くらい。例えば、単にログを共有したいくらいであれば、バッチ処理にはなりますが、エクスポート機能を使うと安く済みます。
tasotasoso.hatenablog.com

ちなみに、Lambdaだと、エクスポートした後に続けて処理をしようとするとstep functionsなどで頑張らないといけませんが、最近はFargateもできたので、コンテナで行うと良いかもしれません。これは記事があまりなかった気がするので、時間があれば実験してみます。

AWSサービスが代理で行うリクエストは、aws:SourceIp キーを適用しないIAMポリシー設定

aws:ViaAWSService キーで、AWSサービスが代理で行うリクエストは、IP制限を適用しないIAMポリシー設定にする設定です。

aws:SourceIp キーの注意点

aws:SourceIp キーは特に企業などで、社内ネットワーク外から操作ができないような設定をするために、よく使われていると思います。
例えばこんな感じです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PrincipalPutObjectIfIpAddress",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-service-bucket/*",
            "Condition": {
                "IpAddress": {"aws:SourceIp": "123.45.167.89"}
            }
        }
    ]
}

AWSサービスが代理で操作を行う際などに、意図しないエラーが発生することがありました。
dev.classmethod.jp

これは、AWS サービスがプリンシパルの代わりに呼び出しを行う場合、aws:SourceIpキーの制限をつけたポリシーによってアクセスを拒否されるためです。

AWS グローバル条件コンテキストキー - AWS Identity and Access Management

aws:SourceIp 条件キーをポリシーで使用して、プリンシパルが指定された IP 範囲内からのみリクエストを行うことを許可できます。ただし、AWS サービスがプリンシパルの代わりに呼び出しを行う場合、このポリシーはアクセスを拒否します。

今の公式ドキュメントでは記載がないですが、先にリンクを貼ったClassmethodさんの記事を見ると、AWSサービスのIPアドレスがリクエスト元と認識されてしまうことが具体的な原因のようなので、下の図のようなイメージを持つと良さそうです。

f:id:tasotasoso:20200418212852p:plain:w500
意図しないエラーの原因

aws:ViaAWSService キー

February 20, 2020に追加されたキーです。
aws:ViaAWSService キーと一緒に aws:SourceIp を使用することで、プリンシパルによって直接行われたリクエストにのみソース IP 制限が適用されるようにできます。
例えば、以下のようなポリシーをつけることで、IP制限を行いつつ、AWSサービスが代理でリクエストを送る際はIP制限を適用しないことができます。

From https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_condition-keys.html

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PrincipalPutObjectIfIpAddress",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-service-bucket/*",
            "Condition": {
                "Bool": {"aws:ViaAWSService": "false"},
                "IpAddress": {"aws:SourceIp": "123.45.167.89"}
            }
        },
        {
            "Sid": "ServicePutObject",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-service-bucket/*",
            "Condition": {
                "Bool": {"aws:ViaAWSService": "true"}
            }
        }
    ]
}


Amazon EC2AWS Glue、AWS Lake Formation、AWS OpsWorksでは未対応のようです。

参考文献

docs.aws.amazon.com

CloudWatchLogsからクロスアカウントでS3にエクスポートする

別のアカウントのCloudWatch Logsに格納されたログデータを、別のアカウントのS3に持ってくる場合です。
自分のアカウント内の記事はよくありますが、クロスアカウントでやってみた報告がないので書きました。

やりたいこと

以下のような構成を実現したいです。

f:id:tasotasoso:20200418182126p:plain:w500
やりたいこと

CloudWatch Logsの中身を取り出す方法

AWSサービスを使ってCloudWatch LogsからS3に中身をエクスポート方法は、調べた限りだと以下の2つがあります。

ちなみに、3rdパーティのログコレクタまで視野に入れているなら、fluentdなどを使って取り出すことも可能です(この場合は、実質的にはSDKを使っている)。
今回は、「エクスポート機能を使う方法」を説明します。

サブスクリプションフィルタを使った方法はとても便利ですが、サブスクリプションフィルタがロググループに1つしかつけられないため、すでに使われている場合は実現できません。その場合は、簡単に実現できるエクスポート機能を使う方法が有力な候補になります。


エクスポート機能を使う

公式の方法がドキュメントにあるため、こちらを参考に行います。
docs.aws.amazon.com
バケットポリシーなど、私の記載が間違っているかもしれないので、こっちも見てからやって見てくださいね

コンソールで行う方法を説明します。


1. エクスポート先のS3に権限を設定する。
まず、エクスポート先にS3バケットを作成します。今回は東京リージョンにmy-exported-logstというバケットを作り、random-stringという階層にエクスポートするとします。

次に、このバケットに、バケットポリシーをつけます。アカウントIDが111111111111のアカウントに所属するCWLExportUserというユーザーを使ってエクスポートするとします。

バケットポリシーは、以下のようになります。

#Refer https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/S3ExportTasksConsole.html

{
    "Version": "2012-10-17",
    "Statement": [
      {
          "Action": "s3:GetBucketAcl",
          "Effect": "Allow",
          "Resource": "arn:aws:s3:::my-exported-logs",
          "Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" }
      },
      {
          "Action": "s3:PutObject" ,
          "Effect": "Allow",
          "Resource": "arn:aws:s3:::my-exported-logs/random-string/*",
          "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } },
          "Principal": { "Service": "logs.ap-northeast-1..amazonaws.com" }
      },
      {
          "Action": "s3:PutObject" ,
          "Effect": "Allow",
          "Resource": "arn:aws:s3:::my-exported-logs/random-string/*",
          "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } },
          "Principal": { "AWS": "arn:aws:iam::111111111111:user/CWLExportUser" }
      }
    ]
}

CloudWatch LogsからGetBucketAclとPutObjectする許可に加え、CWLExportUserからもPutObjectする権限をつけます。もしユーザーではなくロールで行う場合は、3つ目のJSONのConditionキーのバリューをロールのarnに変えるとよいでしょう。


2. エクスポートする
続いて、エクスポートする側のアカウントからエクスポート操作を行います。
この時、CloudWatch LogsとS3にフルアクセスのついたユーザーでログインします。この権限がないとエクスポート時に弾かれてしまいます。

CloudWatch Logsの画面に入り、エクスポートするロググループを選択して[アクション]から[データを Amazon S3 にエクスポートする]を押すと、以下のポップアップが表示されます。


f:id:tasotasoso:20200418182218p:plain:w500
クロスアカウントでのエクスポート設定

以下のように設定を入力します。

  • ①で「別のアカウント」を選択する
  • ②でmy-exported-logsを入力する
  • ③でrandom-stringを入力する

[データのエクスポート]を押すことで、エクスポートが実行できます。


問題がある場合は、[データのエクスポート]押下時にエラーが表示されます。
もし公式ドキュメントも見て、バケットポリシーやロールにつけたポリシーなども完璧に合っているなら、IP制限などがかかっていないか確認すると良いです。

参考・関連文献

fluentdでCloudWatch Logsの中身を取り出す。
github.com

Lambda + Step Functionsでエクスポートする。
dev.classmethod.jp
qiita.com

CloudWatchアラームのアラーム名を日本語にすると、SNSの通知メールがおかしくなる

CloudWatchアラームのアラーム名を日本語にすると、SNSの通知メールがおかしくなる件について調査しました。

背景

システム監視でCloudWatchアラームを使い、問題があった時にSNS経由でアラートメールを飛ばしています。
基本的に英語でメールしていますが、あるとき運用メンバーの要望で、通知する文面が日本語になるよう、CloudWatchアラームのアラーム名と説明を変えたところ、文字化けしたような変なメールが届いたことがありました。

Webで調べたところ、意外と同じ問題に関する記事がなかったので、自分で調べてみました。

結論

CloudWatchアラームのアラーム名:英語のみ
CloudWatchアラームのアラームの説明:日本語もいけそう

ただ、結局アラームの説明に関する制限の記載は明記がなかったので、本当に日本語OKかは運用してみてになりそうです。

調べた情報

まず、CloudWatchのアラーム名ですが、 ASCII 文字のみを使用するという制限がありました。

アラーム名には ASCII 文字のみを使用する必要があります。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html

CloudWatchのアラーム名は、SNSの件名になります。SNSの件名の制限を調べてみると、こちらもASCIIのみでした。
これは、各SNSトピック > [メッセージの発行] > [件名]から分かります。

f:id:tasotasoso:20200405151540p:plain
SNSメール件名の制限


一方、Body部にはマルチバイトが使えそうです。

ASCII エンコーディングを使用するメッセージには、最大 140 文字を含めることができます。UCS-2 エンコーディングを使用するメッセージには、最大 70 文字を含めることができます。

https://aws.amazon.com/jp/sns/faqs/

こちらは少し昔の記事になりますが、Body部にはUnicodeが使えることが書いてあり、もともとこういう仕様になっていたことが分かります。

SubjectはASCII文字列にしてください。Body部は8KBまでのUTF-8文字列を設定できます。

Amazon SNS による通知サービス | Developers.IO

以上から、アラート名は英語、説明を日本語にすることで、日本人メンバーにも優しいアラートメールが発行できそうです。