【TypeScript + AWS】CloudWatchLogsからデータを引っ張りたかったんだ
こんばんは、葛の葉です。
今回はAWSのCloudWatchLogsにたまったログに検索をかけ、それを吐き出すということをやっています。いくつか大変なことがあったので、それを書いていこうと思います。
CloudWatchLogsクラスからインスタンスを作る前に
awsのsdkを使うにはconfigにユーザー情報を渡してあげる必要があります。ユーザーの情報をいくつか渡す方法にcredentialsファイルを用いた認証方法があります。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html
credentialsファイルを用いた認証を行う場合は、awsのGlobalConfigInstanceにSharedIniFileCredentialsクラスを使用する必要があります。
なお、SharedIniFileCredentialsのコンストラクタにはcredentialsファイルに記載したプロファイルを指定することができます。
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というものになります。
StartQueryRequest内のstartTimeとendTimeはそれぞれ検索の開始と終わりの日時の情報を渡すのですが、これがUnixタイムを用います。
LinuxでUnixタイムを用いるにはDateオブジェクトのgetTimeメソッドを用います。
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という型で受け取れます。
getQueryResultsにはstatusがあり、"Scheduled"|"Running"|"Complete"|"Failed"|"Cancelled"のいずれかがあります。
検索結果をWebAPIで提供しているみたいなので、WebAPIを読みに行くことはすぐ終わるのですが、その検索結果がWebAPIに反映されるのはちょっと時間がかかります。Completeとなれば完了するので、そのステータスになるまで何度か叩いてあげます。
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に載ってないこともあるんだ
巨人の肩に乗るってのはこういう大変なことがあるんですねぇ。いや、ずいぶん楽で便利させてもらってますけどね。ドキュメント読んで頑張りましょう〜