ぺい

渋谷系アドテクエンジニアの落書き

CloudWatch Logs Insights 使い方と注意点(Go)

f:id:tikasan0804:20181226194654p:plain

高速でインタラクティブなログ分析!!!!

新機能 – Amazon CloudWatch Logs Insights – 高速でインタラクティブなログ分析 | Amazon Web Services ブログ

仕事で導入する機会があり、その時の使用感とハマりポイントをまとめてみました。

ユースケース

ログがレコード数6,500,000/1時間くらいあるものから、特定の文字列にヒットするものを取得したい。フルスキャンしなくていいので、1日の中から10件の対象ログを探したい。

使用感や注意点

※利用したログでの使用感です

  • 日付の範囲を1日とかにすると、そこまで早くない(検索完了まで3分〜4分はかかった)
  • 従来のログ検索だと、5分のレンジで検索しても、15分以上かかっていたけど、Insightsなら、10秒程度で検索が完了した
  • リアルタイムに何か検知するのは無理。インデックスを作成しているか何かで、3分くらい経たないと検索結果に出てこなかった(インデックスが済むと速い)
  • リアルタイムに検知したいなら、サブスクリプションフィルタのが優れている
  • 検索結果が返ってくるまでの状態管理に、少しコツが必要だった(コードで説明する)
  • limitがクエリにも、APIインターフェイスにも用意されてるけど、使い分けが分からない
  • フルスキャンして、高速検索はBigQueryでやろう

Goでの実装例

今回は、Golangで実装した例です。動作などは、godocを見れば、大体雰囲気わかります。
cloudwatchlogs - GoDoc

github.com

全体の流れ

  1. StartQueryInput でクエリを作成する
  2. StartQueryStartQueryInput に渡し、queryId を受け取る(この時、既にクエリが実行開始している)
  3. GetQueryResults を実行して、実行中のクエリの結果を受け取る
  4. 実行中のクエリのステータスを見て、結果を返すか決める(あとで、取り上げる)
  5. 結果を返す

4. 実行中のクエリのステータスを見て、結果を返すか決める

https://github.com/pei0804/go-cloud-watch-logs-insights/blob/be35e2e44d010a8fbc7764a07e6b74a23d12b650/main.go#L85-L118

func getQueryResultsUntilCompleate(cwl *cloudwatchlogs.CloudWatchLogs, queryId string, limit int) (*cloudwatchlogs.GetQueryResultsOutput, error) {
    getQueryResultInput := &cloudwatchlogs.GetQueryResultsInput{}
    getQueryResultInput.SetQueryId(queryId)
    for {
        getQueryResultOutput, err := cwl.GetQueryResults(getQueryResultInput)
        if err != nil {
            return nil, err
        }
        time.Sleep(5 * time.Second)
        switch *getQueryResultOutput.Status {
        case "Running":
            if len(getQueryResultOutput.Results) < limit {
                continue
            }
            stopQueryInput := &cloudwatchlogs.StopQueryInput{}
            stopQueryInput.SetQueryId(queryId)
            stopResult, err := cwl.StopQuery(stopQueryInput)
            if err != nil {
                return nil, fmt.Errorf("stop query error=%s status=%v", err.Error(), stopResult)
            }
            return getQueryResultOutput, nil
        case "Scheduled":
            continue
        case "Failed":
            return nil, errors.New("job failed")
        case "Cancelled":
            return nil, errors.New("job cancelled")
        case "Complete":
            return getQueryResultOutput, nil
        default:
            return nil, fmt.Errorf("unknown status: %s", *getQueryResultOutput.Status)
        }
    }
}

なぜ、forループで待ち続けているのか

StartQuery を実行すると、クエリが走り始めます。そして、GetQueryResults でクエリの状態を取得します。この時点では、完了していないことがほとんどです。そのため、time.Sleep(5 * time.Second) などして、少し時間を置いてから再度取得しています。

ログの量にもよりますが、何周かすると、終了し、Completeに入ります。ステータスの種類はドキュメントに書いてあります。
GetQueryResults - Amazon CloudWatch Logs

Runningで途中でreturnしているのは?

途中でlimit来たら返しているかというと、全部見なくて良いので、あった結果をとりあえず、早く返してほしかった。

  • 1日くらいのレンジで見ていたので、全部見るの待つと、とても時間がかかる
  • limit 10と設定していても、10件あったらCompleteではなく、全部の結果を受け取った上で、ソートし直すなどをしてるっぽく、指定範囲をフルスキャンになり、時間がかかってしまう