表題のテーマ、過去にもCloudWatchアラーム通知メールのカスタマイズについて書いたが、表示時刻がUTCなのでJSTに変換しようと考えた。
過去記事
CloudWatchアラーム + SNSからのメール本文をカスタマイズする(2)
CloudWatchアラーム + SNSからのメール件名をカスタマイズする
CloudWatchアラームから渡されるeventの、元データの時刻表示は例えば'2021-10-24T09:35:10Z’となっている。これをJSTにするのに手っ取り早いのはpytzを使う方法だが、諸事情により標準ライブラリの範囲でやる必要がある。
で、試行錯誤。当初datetime型にしてからJSTに変換しようとしたがいいやり方が見つからなかったため「unixタイムスタンプに変換後、JSTに変換」とすることにした。
from datetime import datetime, timezone, timedelta
from dateutil import parser
JST = timezone(timedelta(hours=+9), 'JST')
utcstr = '2021-10-24T09:35:10Z'
utcstr_parsed = parser.parse(utcstr)
#UNIXタイムスタンプに変換
ux_time = utcstr_parsed.timestamp()
#int型にする
epoch = int(ux_time)
#JSTに変換
dt = datetime.fromtimestamp(epoch).replace(tzinfo=timezone.utc).astimezone(tz=JST)
print(dt)
2021-10-25 03:35:10+09:00
当初JSTに変換した後の時間が変だ+18時間になってる何故だ、と悩んだが、拠点にした時間から+18時間になるのはおそらく実行環境がJSTだから。UTCの環境でやれば+9時間になるんだろう。くそ、こんなことで数時間週末を無駄にした。俺の休息時間はいつなんだ?
ともあれ、修正したのが以下。コメントの「時刻変換」と、「件名に投入するアラーム名を抽出」を追加した。前回はメール件名規則を「任意の文字列 + 発生契機 + 対象リソース(dimention)」としていたが、発生契機はいらないから代わりにアラーム名にした。
lambda_function.py (時刻表示JSTバージョン)
import boto3
import json
import os
import re
from botocore.exceptions import ClientError
from datetime import datetime, timezone, timedelta
from dateutil import parser
print('Loading function')
sns_arn = os.environ['SNS_TOPIC_ARN']
def lambda_handler(event, context):
data = event
s = json.dumps(data)
e = json.loads(s)
print(e)
# eventから項目を抽出
t = e['time']
trig = e['detail-type']
alarm = e['resources']
# 時刻変換
JST = timezone(timedelta(hours=+9), 'JST')
utcstr_parsed = parser.parse(t)
ux_time = utcstr_parsed.timestamp()
epoch = int(ux_time)
# unixタイムスタンプをJSTに変換。dtはこの時点でdatetime.datetime型
dt = datetime.fromtimestamp(epoch).replace(tzinfo=timezone.utc).astimezone(tz=JST)
# dtを整形
dt_str = dt.strftime('%Y-%m-%d %H:%M:%S')
# 件名に投入するアラーム名を抽出
alm_list = alarm[0].split(':')
alm_name = alm_list[-1]
# 「理由」となる詳細抽出
reason = e['detail']['state']['reason']
# リソース(ここではインスタンスID)を抽出し、文字列整形
resource = e['detail']['configuration']['metrics'][0]['metricStat']['metric']['dimensions']
res_str = json.dumps(resource)
res = re.sub(r"[{}\"]", "", res_str)
# 件名整形
subject_str = "本番環境アラーム " + alm_name + " - " + res
# メッセージ本文整形
fix_msg = "以下のアラームが発生しました" + "\n"
trig_msg = "発生契機:" + "\n" + trig
time_msg = "発生時刻(JST):" + "\n" + dt_str
alm_msg = "アラームARN:" + "\n" + alarm[0]
res_msg ="対象リソース:" "\n" + res
dtl_msg ="理由:" "\n" + reason
msg = fix_msg + "\n\n" + trig_msg + "\n\n" + time_msg + "\n\n" + alm_msg + "\n\n" + res_msg + "\n\n" + dtl_msg
try:
sns = boto3.client('sns')
#SNS Publish
publishResponse = sns.publish(
TopicArn = os.environ['SNS_TOPIC_ARN'],
Message = msg,
Subject = subject_str
)
except Exception as e:
print(e)
上記コードにてLambdaを再デプロイしてec2インスタンスでCPU負荷発生させたところ、一応期待値となるメールが届いた。画面下の「理由」に記載の時刻はUTCとなっているが、「発生時刻(JST)」は日本時刻表記である。(ほぼ実際にCPU負荷をかけた時間)
参考
[Python]UNIX秒(UTC)をISO8601(JST)に変換する
Pythonで日付をunixtimeに変換する方法【初心者向け】
まぁこんなことやったところで誰も感謝もしてくれないがね。映画"Taxi Driver"のトラヴィスの気持ちも分かるってもんだ。(…若干くされ気分)