Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【TypeScript + AWS】CloudWatchLogsからデータを引っ張りたかったんだ

こんばんは、葛の葉です。

今回はAWSのCloudWatchLogsにたまったログに検索をかけ、それを吐き出すということをやっています。いくつか大変なことがあったので、それを書いていこうと思います。

CloudWatchLogsクラスからインスタンスを作る前に

awssdkを使うにはconfigにユーザー情報を渡してあげる必要があります。ユーザーの情報をいくつか渡す方法にcredentialsファイルを用いた認証方法があります。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html

credentialsファイルを用いた認証を行う場合は、awsのGlobalConfigInstanceにSharedIniFileCredentialsクラスを使用する必要があります。

なお、SharedIniFileCredentialsのコンストラクタにはcredentialsファイルに記載したプロファイルを指定することができます。

docs.aws.amazon.com

import * as aws from "aws-sdk";

// CredentialFailsを./aws/から読み込む明示的処理。
aws.config.credentials = new aws.SharedIniFileCredentials({profile: "kuzunoha"});
const clw = new aws.CloudWatchLogs();

credeantialsファイル

[kuzunoha]
aws_access_key_id = ******
aws_secret_access_key = ******

configファイル

[kuzunoha]
region = ap-northeast-1
output = json

大変なこと

aws cliだと勝手にcredentialsファイル読んでくれた気がするのに、同じようにsdkでも読んでくれたらよかったのになーと。credentialsの情報が誤ってんのかとずっと思ってました。はー。

startQuery関数に渡す時間はEpoch時間から引いた時間・・・?

CloudWatchLogsクラス内のstartQueryメソッドの引数となるインターフェイスはStartQueryRequestというものになります。

docs.aws.amazon.com

StartQueryRequest内のstartTimeとendTimeはそれぞれ検索の開始と終わりの日時の情報を渡すのですが、これがUnixタイムを用います。

ja.wikipedia.org

LinuxUnixタイムを用いるにはDateオブジェクトのgetTimeメソッドを用います。

developer.mozilla.org

import * as aws from "aws-sdk";

const logGroupName = "あなたの素敵なロググループ";
const queryString = "あなたの素敵なクエリ";

const startTime = new Date("あなたの検索したい時間").getTime();
const endTime = new Date("あなたの検索したい時間").getTime();

// CredentialFailsを./aws/から読み込む明示的処理。
aws.config.credentials = new aws.SharedIniFileCredentials()
const clw = new aws.CloudWatchLogs();

(async () => {
  const req = await clw.startQuery({ startTime, endTime, logGroupName, queryString }).promise()
  // 処理に続く
})();

大変なこと

getTime関数はミリ秒を含めるけど、startTimeでミリ秒込みで良いのかなんとも書かれていないんですよね。。。。

getQueryResults関数は何度も叩こう。

getQueryResults().promise() はプロミスでWebAPIを叩いているようで、その値はGetQueryResultsResponseという型で受け取れます。

docs.aws.amazon.com

getQueryResultsにはstatusがあり、"Scheduled"|"Running"|"Complete"|"Failed"|"Cancelled"のいずれかがあります。

検索結果をWebAPIで提供しているみたいなので、WebAPIを読みに行くことはすぐ終わるのですが、その検索結果がWebAPIに反映されるのはちょっと時間がかかります。Completeとなれば完了するので、そのステータスになるまで何度か叩いてあげます。

docs.aws.amazon.com

import * as aws from "aws-sdk";

const logGroupName = "あなたの素敵なロググループ";
const queryString = "あなたの素敵なクエリ";

const startTime = new Date("あなたの検索したい時間").getTime();
const endTime = new Date("あなたの検索したい時間").getTime();

// CredentialFailsを./aws/から読み込む明示的処理。
aws.config.credentials = new aws.SharedIniFileCredentials()
const clw = new aws.CloudWatchLogs();

const wait = (ms:number) => {
    return new Promise(resolve => setTimeout(resolve, ms));
};

(async () => {
  const req = await clw.startQuery({ startTime, endTime, logGroupName, queryString }).promise()
  let response: aws.CloudWatchLogs.GetQueryResultsResponse;
  // response.statusがComplete,Failed,Cancelledの何れかになるまで何度か叩いて確認する。
  do {
    await wait(1 * 1000);
    response = await clw.getQueryResults({ queryId: req.queryId }).promise();
  } while (["Scheduled", "Running"].includes(response.status));
  
  console.log(response);
})();

たいへんだったこと

何回かたたかなきゃいけないんだ。。。。

ピンポイントな情報はQiitaやStack Overflowに載ってないこともあるんだ

巨人の肩に乗るってのはこういう大変なことがあるんですねぇ。いや、ずいぶん楽で便利させてもらってますけどね。ドキュメント読んで頑張りましょう〜