始めに
みなさん、CloudWatchLogsInsight使っていますか?私は正直、ほとんど使いこなせていません。
やっていることといえば、
filterでエラー文言を引っ掛ける。
期間をカスタムで見たい範囲より少し広めに取る。(ピンポイントすぎると見つからないことあるので)
ヒットしたログのログストリーム名、リクエストID(Lambdaが多いので)などをメモって、改めて該当ログを通しで確認する。
のみです。
つまりはこの程度。
fields @timestamp, @message, @logStream, @log
| filter @messeage like /ERROR/
| sort @timestamp desc
| limit 20
特にこれで困ってはいないのですが、とある案件で、
ログをダッシュボードに出したい。
エラーの数を見える化したい。
といった調査・検証をすることになり、未知の領域へ一歩踏み出すことになりました。
メッセージをparseする
CloudWatchLogsはサービス毎に出力フォーマットの大枠が決まっています。内容は公式の以下の通り。https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData-discoverable-fields.html
決まっているものは、クエリ構文の「field」で@messageなど「@XXX」の形で指定することで取得できます。
しかし、この@messageには実際のアプリケーションで出力した内容がそのまままるごと入ることになるため、エラーの件数を集計したいみたいな場面ではちょっとそのままでは扱えません。
しかしさすがAWSさん、そこはちゃんと考えてあります。こういうときに使うのがクエリ構文の「parse」です。
parseは以下の2通りの書き方ができます。
glob形式
正規表現形式
それぞれ説明します。
glob形式
ファイル検索などでよくあるやつです。
例えば、こんなメッセージがあったとします。(var/log/messagesの内容)
メッセージを見ると、一定の書式で成り立っていることがわかります。
Sep 14 09:41:23 ip-10-0-0-84 systemd[1]: Finished refresh-policy-routes@ens5.service - Refresh policy routes for ens5.
フィールド | 意味 | 値の例 |
1 | ログ出力した月 | Sep |
2 | ログ出力した日 | 14 |
3 | ログ出力した時刻 | 09:41:23 |
4 | ログ出力したホスト | ip-10-0-0-84 |
5 | ログ出力したプロセス名 | systemd |
6 | ログ出力したプロセスID | 1 |
7 | 出力メッセージ | Finished refresh-policy-routes@ens5.service - Refresh policy routes for ens5. |
これをparseするには、
fields @timestamp, @message, @logStream, @log
| parse @message "* * * * *[*]: *" as @logmon,@logday,@logtime,@host,@process,@pid,@logmsg
| display @logmon,@logday,@logtime,@host,@process,@pid,@logmsg
でできます。アスタリスクで指定したところが順次、as以降の変数にセットされるイメージです。
みなれない「display」について、これはなくてもfieldsで選んだものとパースした結果がでてきますが、最終出力の項目をこの例のように最後に選択することができます。
正規表現形式
比較的単純なケースはglobで事足りますが、もっと込み入った解析が必要となる場合、正規表現にも対応しています。名前付きキャプチャグループという書き方で指定することになります。
名前付きキャプチャグループとは、以下のような形で()で括った中の正規表現にヒットするものを<>内の変数名として抜き出すものになります。
(?<変数名>正規表現)
先程のglobと同じことを正規表現で行うには、
fields @timestamp, @message |
parse @message /(?<logmon>[^\s]+) (?<logday>[^\s]+) (?<logtime>[^\s]+) (?<host>[^\s]+) (?<process>[^\s\[\]]+)\[(?<pid>[^\s\[\]]+)\]: (?<logmsg>.+)/ |
display logmon,logday,logtime,host,process,pid,logmsg
といった具合になります。
正規表現のところはちょっと込み入った感じで書きましたが、globでできるくらいなので、実際はもっとシンプルでも抜き出せるかもしれません。
また、glob形式と違って変数名に@がつかないです。
ログを集計する
ここまでで、メッセージを解析して要素ごとに分解できました。次にホスト名毎、プロセス毎の件数を取得してみたいと思います。
集計は「stats」でできます。
fields @timestamp, @message |
parse @message /(?<logmon>[^\s]+) (?<logday>[^\s]+) (?<logtime>[^\s]+) (?<host>[^\s]+) (?<process>[^\s\[\]]+)\[(?<pid>[^\s\[\]]+)\]: (?<logmsg>.+)/ |
stats count(*) by host,process
まとめ
使い倒そうと言いつつ、初歩やんけ、というツッコミがありそうですが、千里の道も一歩からということでご容赦ください。
調べてみるといろんなことができそうです。https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html
ログはトラブルシューティングで避けては通れないもの。誰かのお役に立てれば幸いです。
Comments