前提
・AWS CLIがインストールされて、初期設定が完了できていること
・jqがインストールされていること
・実行OS:Ubuntu 20.04 On Windows
背景
同じログストリームにログがたまり続ける場合、-start-from-headを使うと、かなり時間がかかります。
この場合、最新ログイベントから次の最新ログイベントの順番で取得すると、速くなります。
・nextForwardToken
説明:最古のログから順番にログを取得する場合に利用する。–start-from-headを使う
nextForwardTokenを使った記事
・nextBackwardToken
説明:最新のログから順番にログを取得する場合に利用する。–no-start-from-headを使う
シェル内容
VPCFlowLogsを例にシェルを作成してみました。
ファイル名:Get_VPCFlowLogs_OneDayAgo_EveryLogStream.sh
# !/bin/bash
NORMAL_END=0
ABNORMAL_END=1
# 終了処理
END_FUNC()
{
case ${1} in
${NORMAL_END})
# 正常(返却値:0)
echo "=====正常終了しました。===="
;;
${ABNORMAL_END})
# 異常
echo "=====異常終了しました。===="
;;
esac
exit ${1}
}
# ログ出力先
EXE_DIR=/mnt/c/AWSLogs/LOG
if [ ! -d $EXE_DIR ]; then
# 存在しない場合、作成する
mkdir -m 777 $EXE_DIR
fi
# ログ日付(CloudwatchlogsのLocalタイムスタンプ(日本時間))
LOG_DATE=$(TZ=UTC-9 date -d '1 days ago' '+%Y-%m-%d')
# ログ日付の前の日(CloudwatchlogsのLocalタイムスタンプ(日本時間))
LOG_DATE_ONE_DAY_AGO=$(TZ=UTC-9 date -d "$LOG_DATE 1 days ago" '+%Y-%m-%d')
# ログ日付の次の日(CloudwatchlogsのLocalタイムスタンプ(日本時間))
LOG_DATE_ONE_DAY_AFTER=$(TZ=UTC-9 date -d "$LOG_DATE 1 day" '+%Y-%m-%d')
# シェル実行日付
SYS_DATE_CURRENT=$LOG_DATE_ONE_DAY_AFTER
# CloudWatchLogsのLasteEventTimeFrom
LOG_STREAMS_DATE_FROM=$(echo "${LOG_DATE_ONE_DAY_AGO}" '23:59:00')
# CloudWatchLogsのLasteEventTimeTo
LOG_STREAMS_DATE_TO=$(echo "${SYS_DATE_CURRENT}" '23:59:00')
# CloudWatchLogsのLasteEventTimeFrom(Unixtime)
UT_FROM=`date -d "$LOG_STREAMS_DATE_FROM" +%s`000
# CloudWatchLogsのLasteEventTimeTo(Unixtime)
UT_TO=`date -d "$LOG_STREAMS_DATE_TO" +%s`000
# 標準出力(確認用)
echo "LOG_DATE:" $LOG_DATE
echo "LOG_DATE_ONE_DAY_AGO:" $LOG_DATE_ONE_DAY_AGO
echo "LOG_DATE_ONE_DAY_AFTER:" $LOG_DATE_ONE_DAY_AFTER
echo "LOG_STREAMS_DATE_FROM:" $LOG_STREAMS_DATE_FROM
echo "LOG_STREAMS_DATE_TO:" $LOG_STREAMS_DATE_TO
echo "UT_FROM:" $UT_FROM
echo "UT_TO:" $UT_TO
LOG_GROUP_NAME="KbyDev3-Tyo-Clwlog-VPCFlowLogs"
OUT_LOG_GROUP_FILENAME=$EXE_DIR/"${1}"_ALL.log
# jqのselect条件
COND="(($UT_FROM <= .firstEventTimestamp) and (.firstEventTimestamp <= $UT_TO))"
COND="$COND or (($UT_FROM <= .lastEventTimestamp) and (.lastEventTimestamp <= $UT_TO))"
COND="$COND or ((.firstEventTimestamp <= $UT_FROM) and ($UT_TO <= .lastEventTimestamp))"
# 全ロググループ
aws logs describe-log-groups | jq '.logGroups[] | .logGroupName' -r | less > $EXE_DIR/LOG_GROUP_ALL.txt
# 対象ロググループ
grep -e $LOG_GROUP_NAME $EXE_DIR/LOG_GROUP_ALL.txt > $EXE_DIR/LOG_GROUP.txt
LOGGROUP_cat=`cat $EXE_DIR/LOG_GROUP.txt`
while read TEMP_OUT_LINES
do
echo "ロググループ名:"$TEMP_OUT_LINES
aws logs describe-log-streams --log-group-name $TEMP_OUT_LINES --order-by LastEventTime --no-descending | jq -r ".logStreams[] | select($COND) | .logStreamName" | while read LOGSTREAM; do
# ログストリームのログレコードを、タイムスタンプとメッセージのタブ区切りに整形して出力
if [ ! -d $EXE_DIR/${TEMP_OUT_LINES##/*/} ]; then
# 存在しない場合、作成する
mkdir -m 777 $EXE_DIR//${TEMP_OUT_LINES##/*/}
fi
RESPONSE_FILE_NAME="$EXE_DIR/RESPONSE.log"
aws logs get-log-events --log-group-name $TEMP_OUT_LINES --log-stream-name $LOGSTREAM --no-start-from-head > "${RESPONSE_FILE_NAME}"
echo "ロググループ名:"$TEMP_OUT_LINES "ログストリーム名:"$LOGSTREAM
NEXT_TOKEN=$(cat "${RESPONSE_FILE_NAME}" | jq -r '.nextBackwardToken')
RET_CODE=${?}
if [ ${RET_CODE} -ge 1 ]; then
echo "info:対象ログストリーム["${LOGSTREAM}"]のNEXT_TOKEN取得に失敗しました。"
END_FUNC 1
else
echo "info:対象ログストリーム["${LOGSTREAM}"]のNEXT_TOKENを取得しました。"
echo "NEXT_TOKEN:" $NEXT_TOKEN
fi
# NEXT_TOKEN数分でループ
while [ -n "${NEXT_TOKEN}" ]; do
cat "${RESPONSE_FILE_NAME}" | jq -r '.events[] | [(.timestamp/1000+32400 | strftime("%Y-%m-%d %H:%M:%S")), .message] |@tsv' | sed 's/\\n$//' >> $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_ALL.log
aws logs get-log-events --log-group-name "${TEMP_OUT_LINES}" --log-stream-name "${LOGSTREAM}" --no-start-from-head --next-token "${NEXT_TOKEN}" > "${RESPONSE_FILE_NAME}"
NEXT_TOKEN=$(cat "${RESPONSE_FILE_NAME}" | jq -r '.nextBackwardToken')
RET_CODE=${?}
if [ ${RET_CODE} -ge 1 ]; then
echo "info:対象ログストリーム["${LOGSTREAM}"]のNEXT_TOKEN取得に失敗しました。"
END_FUNC 1
else
echo "info:対象ログストリーム["${LOGSTREAM}"]のNEXT_TOKENを取得しました。"
echo "NEXT_TOKEN:" $NEXT_TOKEN
fi
HEAD_FIRST_TIMESTAMP=$(cat "${RESPONSE_FILE_NAME}" | jq -r '.events[].timestamp' | sort -r | head -n 1)
echo "HEAD_FIRST_TIMESTAMP:" $HEAD_FIRST_TIMESTAMP
# 空ではない場合
if [ -n $HEAD_FIRST_TIMESTAMP ]; then
# ログストリームの開始時間より古い場合、ループを終了
# if [ $(( $HEAD_FIRST_TIMESTAMP / 1000 )) -lt $(( $UT_FROM / 1000 )) ]; then
if [ $(( $HEAD_FIRST_TIMESTAMP )) -lt $(( $UT_FROM )) ]; then
break
fi
fi
# ログストリームの次ページが存在しない場合、ループを終了
if [[ $(cat "${RESPONSE_FILE_NAME}" | jq -e '.events == []') == "true" ]]; then
break
fi
done
# ソートする
cat $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_ALL.log | sort > $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_ALL_Sorted.log
# 対象ログを抽出
echo "対象日付(抽出条件:抽出対象のタイムスタンプ >= $LOG_DATE && 抽出対象のタイムスタンプ < $SYS_DATE_CURRENT)のログを抽出します。"
awk '$1 >= "'"$LOG_DATE"'" && $1 < "'"$SYS_DATE_CURRENT"'" {print $0}' $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_ALL_Sorted.log > $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_$LOG_DATE.log
# 一時ファイルを削除
rm -f $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_ALL_Sorted.log
rm -f $EXE_DIR/${TEMP_OUT_LINES##/*/}/"${LOGSTREAM}"_ALL.log
rm -f $EXE_DIR/LOG_GROUP_ALL.txt
rm -f $EXE_DIR/LOG_GROUP.txt
rm -f $EXE_DIR/RESPONSE.log
done
done << FILE
$LOGGROUP_cat
FILE
# フォルダの日付
DT_RESULT=$LOG_DATE
# 「/mnt/c/AWSLogs/LOG」の上の階層「/mnt/c/AWSLogs」に移動
cd ${EXE_DIR%/*}
# 一時フォルダを「日付_ログ名」にコピー
cp -r ${EXE_DIR}/$LOG_GROUP_NAME $DT_RESULT"_$LOG_GROUP_NAME"
# 一時フォルダを
rm -rf $(basename ${EXE_DIR})
############################################################################
# 終了処理:戻り値を返却
############################################################################
END_FUNC 0
# RET_CODEが1以上の場合
if [ "${RET_CODE}" -ge "1" ]; then
END_FUNC "${RET_CODE}"
elif [ "${RET_CODE}" == "0" ]; then
END_FUNC "${RET_CODE}"
fi
実行結果
結果のフォルダ構成
ログ内容サンプル
・前のログ(0時)
・後ろのログ(23時)
参考
AWS公式より(get-log-events)
==================================================
-start-from-head | –no-start-from-head (boolean)
値がtrueの場合、最も早いログイベントが最初に返されます。 値がfalseの場合、最新のログイベントが最初に返されます。 デフォルト値はfalseです。
この操作でnextTokenとして前のnextForwardToken値を使用している場合は、startFromHeadにtrueを指定する必要があります。
==================================================
まとめ
get-log-eventsを使う時に、古いログを取得したい場合、nextForwardTokenを、新しいのログを取得したい場合、nextBackwardToken、を使ったほうが効率よく取得できので、分けて使った方がいいと思います。