ChatWorkのAPI使用回数をLambdaとCloudWatch Logsを使って記録する
ChatWorkでBOTアカウントを運用しているのですが、そのBOTのAPIの残り使用回数が知りたいので作りました。
概要
- CloudWatch EventsでLambdaを10分毎に実行
- LambdaからChatWorkのAPIを叩き、レスポンスヘッダーからAPI使用回数を取得
- 取得した使用回数をCloudWatch Logsへ記録
基本的にAWSのリソースは全てTerraform(インフラの構成をコードで記述し、コマンド一発で起動するツール)を使っています。
最終的なコードはGitHubへ上げています。
https://github.com/hareku/terraform-logging-chatwork-api-limit
LambdaからChatWork APIの使用回数をCloudWatch Logsへ保存
今回作成したLambda Functionでの大まかなフローはこちら。
- ChatWorkのAPIを叩いて、次に使用回数がリセットされる時間を取得する
- API回数がリセットされる10秒前まで待機する
- 再度APIを叩いて使用回数を取得し、CloudWatch Logsへ記録
ChatWorkのAPIドキュメントを見れば分かりますが、ChatWorkでは5分間に100回までのAPI使用制限があり、残りの回数などはレスポンスヘッダーで返ってきます。
それを利用しCloudWatch Logsへ「APIの残り回数」を吐き出しています。
ChatWorkのTokenは環境変数で設定しています。
const AWS = require('aws-sdk')
const moment = require('moment-timezone')
const axios = require('axios')
axios.defaults.headers.common['X-ChatWorkToken'] = process.env.ChatWorkToken
exports.handler = async (event, context) => {
try {
// 次にAPI制限がリセットされる10秒前まで待機する
const responseForWaiting = await axios.get('https://api.chatwork.com/v2/me')
const nextResetUnixTime = Number(responseForWaiting.headers['x-ratelimit-reset'])
const waitUnixTime = nextResetUnixTime - moment().tz('Asia/Tokyo').unix() - 10
await new Promise(resolve => setTimeout(resolve, waitUnixTime * 1000))
const responseForLogging = await axios.get('https://api.chatwork.com/v2/me')
const remaining = Number(responseForLogging.headers['x-ratelimit-remaining'])
const putLogParams = {
logEvents: [
{
message: `${remaining}`,
timestamp: nextResetUnixTime * 1000
}
],
logGroupName: 'ChatWorkAPI',
logStreamName: 'APIRemaining'
}
const logsClient = new AWS.CloudWatchLogs()
const sequenceTokenIfError = await logsClient.putLogEvents(putLogParams).promise()
.catch(error => {
if (error && error.code === 'InvalidSequenceTokenException') {
// nextSequenceToken
return error.message.match(/[0-9]+/).pop()
}
return Promise.reject(error)
})
if (sequenceTokenIfError) {
putLogParams.sequenceToken = sequenceTokenIfError
await logsClient.putLogEvents(putLogParams).promise()
}
} catch (error) {
console.error(`[Error]: ${JSON.stringify(error)}`)
return error
}
sequenceToken
CloudWatch LogsにはsequenceTokenというものがあります。
これは二回目以降のログ時(ログストリームの2つ目のログ以降)に必ず必要なパラメーターです。
ログ時のレスポンスに毎回sequenceTokenが返ってきますので、次のログ時のパラメーターとして使います。
DynamoDBにsequenceTokenを記録して次回のログで利用する、というやり方もできそうですが、面倒なのでCloudWatch LogsのAPIを2回叩き、1回目をsequenceToken取得用、2回目をログ吐き出し用としています。
dependencies
package.jsonのdependenciesは以下。
{
"dependencies": {
"aws-sdk": "^2.270.1",
"axios": "^0.18.0",
"moment": "^2.22.2",
"moment-timezone": "^0.5.21"
}
}
Terraformでの環境構築
今回はTerraformを使って構築しました。
terraform apply
コマンドですぐに構築することができ、またterraform destroy
コマンドによってapplyから構築されたリソースを全て削除できます。ちょっと遊んだ後の片付けが捗りますね。
またLambda Functionのzip化なども自動で行うことが可能です。
今回書いたtfファイルはこちらです。
# IAM Role for Lambda
resource "aws_iam_role" "this" {
name = "RoleForLambda"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "terraform_lambda_iam_policy_basic_execution" {
role = "${aws_iam_role.this.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
# Lambda Function
data "archive_file" "this" {
type = "zip"
source_dir = "lambda_function"
output_path = "lambda_function.zip"
}
resource "aws_lambda_function" "this" {
filename = "${data.archive_file.this.output_path}"
function_name = "logging_chatwork_api_rate_limit"
role = "${aws_iam_role.this.arn}"
handler = "index.handler"
source_code_hash = "${data.archive_file.this.output_base64sha256}"
runtime = "nodejs8.10"
timeout = 300
environment {
variables = {
ChatWorkToken = "${var.chatwork_token}"
}
}
}
# CloudWatch Event
resource "aws_cloudwatch_event_rule" "this" {
name = "schedule-check-chatwork-api-limit"
description = "Schedule the remaining number of the ChatWork API"
schedule_expression = "rate(10 minutes)"
}
resource "aws_cloudwatch_event_target" "this" {
rule = "${aws_cloudwatch_event_rule.this.name}"
arn = "${aws_lambda_function.this.arn}"
}
resource "aws_lambda_permission" "this" {
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.this.function_name}"
principal = "events.amazonaws.com"
statement_id = "AllowExecutionFromCloudWatch"
source_arn = "${aws_cloudwatch_event_rule.this.arn}"
}
# CloudWatch Log
resource "aws_cloudwatch_log_group" "this" {
name = "ChatWorkAPI"
}
resource "aws_cloudwatch_log_stream" "this" {
name = "APIRemaining"
log_group_name = "${aws_cloudwatch_log_group.this.name}"
}
resource "aws_cloudwatch_log_metric_filter" "this" {
name = "ChatWorkAPIRemaining"
pattern = "[remaining]"
log_group_name = "${aws_cloudwatch_log_group.this.name}"
metric_transformation {
name = "APIRemaining"
namespace = "ChatWork"
value = "$remaining"
}
}
resource "aws_cloudwatch_metric_alarm" "living_related_50x_critical" {
alarm_name = "chatwork-api-remaining"
comparison_operator = "LessThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "APIRemaining"
namespace = "ChatWork"
period = "900"
statistic = "Minimum"
threshold = "15"
alarm_description = "This metric monitor API remaining"
}
あとがき
今回のTerraformとLambdaをまとめたリポジトリをGitHubに上げているので、ご自由にお使いください。
https://github.com/hareku/terraform-logging-chatwork-api-limit
2018年7月10日 修正
DynamoDBへAPIの使用回数や残り回数などを記録していましたが、CloudWatch Logsへ記録するように修正しました。
メトリクスを使うことによってグラフ化やアラートを設定することができるため、ユースケースとしてはCloudWatch Logsの方が正しそうだからです。
Author And Source
この問題について(ChatWorkのAPI使用回数をLambdaとCloudWatch Logsを使って記録する), 我々は、より多くの情報をここで見つけました https://qiita.com/hareku/items/71960296e07cbe45ff11著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .