表題のテーマ、過去にも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負荷をかけた時間)

 

CWアラーム

 

参考
[Python]UNIX秒(UTC)をISO8601(JST)に変換する
Pythonで日付をunixtimeに変換する方法【初心者向け】

 

まぁこんなことやったところで誰も感謝もしてくれないがね。映画"Taxi Driver"のトラヴィスの気持ちも分かるってもんだ。(…若干くされ気分)

 

BCN


関連がありそうな記事