7839

雑草魂エンジニアブログ

【Python】CloudWatch LogsのログをPythonで自動解析する(AWS CLI)

CloudWatch Logs でログ解析をして、状態監視をするにあたり、AWS CLI でログを取得し、不具合件数の出力までを Python で自動化を行った。意外に簡単で、とても便利だったので、今回紹介する。AWS Consoleでもフィルターなどをかけることができるが、Pandas で操作したりすることで内部の message までを詳細に解析できて便利であった。

背景

IoT 機器のセンサデータを AWS Lambda で内部処理を行いデータベースに保存をしている。 今回、センサ側では送信ログが残っているが、データベースに保存されていないことが発覚し、CloudWatch のログ解析を行うこととした。

f:id:serip39:20200714214111p:plain

構成

今回は、以下を用いて自動化を行った。

大まかな流れは以下の通りである。

  1. subprocess を用いて、 AWS CLI を用いて、CloudWatch Logsのログデータを取得する
  2. ログデータを整形する
  3. Pandas を用いて、センサデータとの比較を行い、解析結果として CSV データを出力する

今回は、1 の「subprocess を用いて、 AWS CLI を用いて、CloudWatch Logs のログデータを取得する」のみを説明する。

subprocessとは

subprocess は、 Python のプログラムから他のアプリを起動したり、実行結果を得たりするモジュールである。すなわち、自分でターミナルに打っているコマンドを Python のプログラムを介して実行することができるのである。まさに自動化を行う際に、有用である。

docs.python.org

使い方

基本的には、 'run' メソッドに実行したいコマンドを渡すことで、コマンドを実行することができる。

import subprocess
import json

p = subprocess.run(["コマンド"],  stdout = subprocess.PIPE,  stderr = subprocess.STDOUT)

data = json.loads(p.stdout.decode('utf8'))

コマンドの部分には、コマンドを文字列の配列で渡す。
例)「ls -a」の場合、['ls', '-a']

run のオプションに指定しているものは、標準出力の stdout標準エラー出力stderr である。 run メソッドの返却値のstdout属性を参照することで、標準出力と標準エラー出力を取得することができる。

また、今回AWS CLIの返却値は jsonデータである。しかしながら、実際にはコマンドで実行して得られる結果は、jsonの文字列データとなっている。プログラム上での取り扱いも踏まえて、json_object(辞書型)に変換して取り扱いやすいようにした。(そのため、 import json を追加している。subprocess だけを使う場合は、不要である。)

AWS CLI Logs

AWS CLI をダウンロードしておく必要があるため、まだダウンロードされていない方は以下を参照ください。

AWS CLI のインストール - AWS Command Line Interface

そして今回、以下の CloudWatch Logs のコマンドを用いた。

docs.aws.amazon.com

たくさん利用できるコマンドがあるが、実際にログを取得する場合に使ったコマンドは、以下の 3つである。

全ての Log events から一斉に期間を絞ってログを取得する場合

filter-log-events
filter-log-events — AWS CLI 1.18.97 Command Reference

様々なパラメータやフィルターの設定も可能であるが、log-group-nameとstart-timeとend-timeを指定してデータ取得が可能である。これは、AWS Console での日付での絞り込みと同様の操作である。

$ aws logs filter-log-events --log-group-name <-GroupName-> --start-time <-UNIX-> --end-time <-UNIX->

Stream名で絞り込んで、ログを取得する場合

get-log-events
get-log-events — AWS CLI 1.18.97 Command Reference

Stream名がわかっている場合は、一番取得したいデータに最速でアプローチすることが可能である。

$ aws logs get-log-events --log-group-name '<-GroupName->' --log-stream-name '<-StreamName->'

Stream名がわからない場合に、まずStream名を取得する場合

describe-log-streams
describe-log-streams — AWS CLI 1.18.97 Command Reference

Stream名のPrefixで絞り込んで、Stream名の一覧を取得することができる。この取得した一覧データを元に、get-log-events を実行することで全てのデータを取得することができる。

$ aws logs describe-log-streams --log-group-name '<-GroupName->' --log-stream-name-prefix '<-Prefix->'

参考プログラム

import subprocess
import json
from datetime import datetime
def getFilteredLogEventList(groupName, start, end):
    cmd = []
    cmd.append('aws')
    cmd.append('logs')
    cmd.append('filter-log-events')
    cmd.append('--log-group-name')
    cmd.append(groupName)
    cmd.append('--start-time')
    cmd.append(str(start))
    cmd.append('--end-time')
    cmd.append(str(end))

    p = subprocess.run(cmd, stdout = subprocess.PIPE,  stderr = subprocess.STDOUT)

    data = json.loads(p.stdout.decode('utf8'))
    log_list = data['events']
    return log_list

if __name__ == '__main__':
    GROUPNAME = '<--- GroupName -->'
    START = '2020/06/01'
    END = '2020/06/30'

    datetimeStart = datetime.strptime(START, '%Y/%m/%d')
    datetimeEnd = datetime.strptime(END, '%Y/%m/%d')
    unixStart = int(datetimeStart.timestamp() * 1000)
    unixEnd = int(datetimeEnd.timestamp() * 1000)

    logList = getFilteredLogEventList(GROUPNAME, unixStart, unixEnd)
    print(logList)

まとめ

Python を用いて、実際に自動化をしてみて、非常に便利であると思えた。AWS Console も操作しやすいが、Python(Pandas)の操作ができるのであれば、こちらの方が詳細まで解析できて非常にいいのではないかと思えた。

それでは、ステキな開発ライフを。