柿食えば鐘が鳴るなり法隆寺

#!/bin/bash
# execCheckLog.sh
#
#-----------------------------------------------------
# Purpose : 指定ファイルに指定文字列が出力された場合チャットワークで報知するシェルスクリプト
#-----------------------------------------------------

# USAGE
if [ "$1" = "help" ]; then
    echo "USAGE:"
    echo "  $0"
    echo ""
    echo "  チャットワークのAPIトークンとルームIDを指定  : $0 0123456789abcdef 123456789"
    exit 0
fi

readonly MSG_FILE_PATH="/tmp/execCheckLog_msg.txt"
readonly CHATWORK_TOKEN=$1
readonly CHATWORK_ROOMID=$2

#-----------------------------------------------------
# チャットワークAPIを利用しメッセージを送る関数です
# @param チャットワークに送るメッセージを記述したファイルのパス
#-----------------------------------------------------
send_chatwork() {
    local -r TARGET_PATH=$1
    local -r MESSAGE=$(cat "${TARGET_PATH}")
    curl -X POST -H "X-ChatWorkToken: ${CHATWORK_TOKEN}" -d "body=${MESSAGE}" "https://api.chatwork.com/v2/rooms/${CHATWORK_ROOMID}/messages"
}

#-----------------------------------------------------
# 所定のフォーマットで記述されたファイルを作成する関数です(指定文字列を検知した際に使用)
# ファイルを作成する理由は、チャートワークAPIにメッセージを送る際改行がスムーズにいくためです。
# @param 検査したファイルのパス(文字列)
# @param 検知した文字列(文字列)
# @param エラーの詳細(文字列)
# @param 検査した行情報(文字列)
#-----------------------------------------------------
create_message() {
    local -r SERVER_NAME=$(hostname)
    local -r MY_IP=$(hostname -I | cut -f1 -d' ')
    local -r FILE_PATH=$1
    local -r TOKEN=$2
    local -r CONTENTS=$3
    local -r ROW_INFO=$4

    cat <<_EOT_ >${MSG_FILE_PATH}
[info]
[title]
【ログ異常検知】${SERVER_NAME}(${MY_IP})
[/title]
ファイルパス:${FILE_PATH}
検査行:${ROW_INFO}
検知文字列:${TOKEN}
詳細:${CONTENTS}

[/info]

_EOT_

}

#-----------------------------------------------------
# 本スクリプトの生存確認の為、所定の時間にチャットワークにメッセージを送る関数です
#-----------------------------------------------------
check_alive() {
    local -r CURRENT_TIME=$(date -d '0 second ago' '+%H%M')
    local -r TARGET_TIME="0700"

    if [ "${CURRENT_TIME}" != "${TARGET_TIME}" ]; then
        return 1
    fi

    cat <<_EOT_ >${MSG_FILE_PATH}
[info]
[title]
【ログ異常検知】${SERVER_NAME}($MY_IP)
[/title]
これは本プログラム自身の稼働チェックです。本日も監視処理を実施中です。

[/info]

_EOT_

    send_chatwork "${MSG_FILE_PATH}"
}

#-----------------------------------------------------
# 指定ファイルに指定文字列が出力された場合チャットワークで報知する関数です
# @param 検査ファイルのPATH
# @param 検知文字列
# @param 検査した行をFIXさせるかどうかを判断するための値(boolean)
# @param 処理番号(int)。FIX行を管理するファイルの名前の一意性を担保するのに使います
#-----------------------------------------------------
check_log() {
    local -r TARGET_FILE_PATH=$1
    local -r CHECK_TOKEN=$2
    local -r IS_UPDATE_LAST_LOG=$3    
    local -r LAST_LOG_LINE_NUM_PATH="/tmp/last_log_line_""$4"".txt"

    if [ -e "${TARGET_FILE_PATH}" ]; then
        echo "PATH:""${TARGET_FILE_PATH}""、に対し処理を開始します..."
    else
        return 1
    fi

    # ログの最新行数を取得して、前回からの出力変化をチェック。
    if [ -e "${LAST_LOG_LINE_NUM_PATH}" ]; then
        #  前回検査時の最終行数を取得
        local -r LAST_LOG_LINE_NUM=$(cat "${LAST_LOG_LINE_NUM_PATH}")
    else
        #   今回の最終行数を取得。0でも良いけどそれだと問題ありそうだからやめぴ
        local -r LAST_LOG_LINE_NUM=$(tr <"${TARGET_FILE_PATH}" ' ' _ | wc -l)
    fi

    local -r CURRENT_LOG_LINE_NUM=$(tr <"${TARGET_FILE_PATH}" ' ' _ | wc -l) # 今回の最終行数を取得
    local -r DIFF_LOG_COUNT=$*1    # 行数差を取得

    # 最終行数に変化がある場合、前回最終行数から今回最終行数の範囲で、検知文字列を確認。
    if [ "$CURRENT_LOG_LINE_NUM" -gt "$LAST_LOG_LINE_NUM" ]; then
        local -r LAST_LOG=$(tail -n $DIFF_LOG_COUNT "${TARGET_FILE_PATH}| nl -v "${LAST_LOG_LINE_NUM}")
        # 検知文字列を確認
        if echo "${LAST_LOG}" | grep "${CHECK_TOKEN}"then
            echo "文字列[""${CHECK_TOKEN}""]が見つかりました。"
            local -r CONTENTS="$(echo "${LAST_LOG}| grep "${CHECK_TOKEN}")"
            create_message "${TARGET_FILE_PATH}" "${CHECK_TOKEN}" "${CONTENTS}" "${LAST_LOG_LINE_NUM}行から${CURRENT_LOG_LINE_NUM}行"
            send_chatwork "${MSG_FILE_PATH}"
        else
            echo "文字列[""${CHECK_TOKEN}""]が見つかりませんでした。"
        fi
    fi

    # 今回の最終行数を$LAST_LOG_LINE_NUM_PATHファイルに上書き
    if "${IS_UPDATE_LAST_LOG}"then
        echo "${CURRENT_LOG_LINE_NUM}" >"${LAST_LOG_LINE_NUM_PATH}"
    fi
}

#-----------------------------------------------------
# check_alive関数を実行します
#-----------------------------------------------------
check_alive

#-----------------------------------------------------
# nginxのaccess.logに対し、指定文字列を格納した配列でループさせて、check_log関数を実行します
#-----------------------------------------------------
readonly CHECK_NGINX_ACCESSLOG_PATH="/var/log/nginx/access.log"
readonly CHECK_NGINX_TOKENS=("fluent" "Fluent" "fatal" "Fatal")
readonly CHECK_NGINX_TOKENS_NUM=${#CHECK_NGINX_TOKENS[*]}
_check_nginx_cnt=0

for _nginx_token in "${CHECK_NGINX_TOKENS[@]}"do
    _check_nginx_cnt=$*2

    if [ "${_check_nginx_cnt}" -eq "$CHECK_NGINX_TOKENS_NUM" ]; then
        check_log "${CHECK_NGINX_ACCESSLOG_PATH}" "${_nginx_token}" true 100
    else
        check_log "${CHECK_NGINX_ACCESSLOG_PATH}" "${_nginx_token}" false 100
    fi
done

#-----------------------------------------------------
# syslogに対し、指定文字列を格納した配列でループさせて、check_log関数を実行します
#-----------------------------------------------------
readonly CHECK_SYSLOG_PATH="/var/log/messages"
readonly CHECK_SYSLOG_TOKENS=("oom" "mysql")
readonly CHECK_SYSLOG_NUM=${#CHECK_SYSLOG_TOKENS[*]}
_check_syslog_cnt=0

for _syslog_token in "${CHECK_SYSLOG_TOKENS[@]}"do
    _check_syslog_cnt=$*3

    if [ "${_check_syslog_cnt}" -eq "$CHECK_SYSLOG_NUM" ]; then
        check_log "${CHECK_SYSLOG_PATH}" "${_syslog_token}" true 101
    else
        check_log "${CHECK_SYSLOG_PATH}" "${_syslog_token}" false 101
    fi
done

readonly CURRENT_YEAR=$(date -d '0 second ago' '+%Y')  # fuelのログのパスを作る用
readonly CURRENT_MONTH=$(date -d '0 second ago' '+%m') # fuelのログのパスを作る用
readonly CURRENT_DAY=$(date -d '0 second ago' '+%d')   # fuelのログのパスを作る用
#-----------------------------------------------------
fuelphpのtasksのlogに対し、指定文字列を格納した配列でループさせて、check_log関数を実行します
#-----------------------------------------------------
readonly CHECK_FUEL_TASKS_LOG_PATH="/home/appname/fuel/app/logs/tasks/""${CURRENT_YEAR}""/""${CURRENT_MONTH}""/""${CURRENT_DAY}"".php"
readonly CHECK_FUEL_TASKS_LOG_TOKENS=("Notice" "fatal" "Fatal")
readonly CHECK_FUEL_TASKS_LOG_TOKENS_NUM=${#CHECK_FUEL_TASKS_LOG_TOKENS[*]}
_check_fuel_tasks_cnt=0

for _fuel_tasks_token in "${CHECK_FUEL_TASKS_LOG_TOKENS[@]}"do
    _check_fuel_tasks_cnt=$*4

    if [ "${_check_fuel_tasks_cnt}" -eq "$CHECK_FUEL_TASKS_LOG_TOKENS_NUM" ]; then
        check_log "${CHECK_FUEL_TASKS_LOG_PATH}" "${_fuel_tasks_token}" true 102
    else
        check_log "${CHECK_FUEL_TASKS_LOG_PATH}" "${_fuel_tasks_token}" false 102
    fi
done

*1:CURRENT_LOG_LINE_NUM - LAST_LOG_LINE_NUM

*2:$_check_nginx_cnt + 1

*3:$_check_syslog_cnt + 1

*4:$_check_fuel_tasks_cnt + 1