#!/bin/bash
# 功能实现：根据requestID以及IP，获得完整的链路日志信息
# 依赖文件：ips.sh
# 存在问题：

# 静态-点播-直播
# 1. 10:00:04 - 10:15:33 - 10:29:01 - 10:30:01 - 10:45:28 - 10:59:55
# 2. 一个小时前 | 两个小时前 | 三个小时前 | 四个小时前 | 八个小时前 | 两天前
# 3. 00:00:00前后

# 各个层级HIT/MISS情况
# - MISS MISS | MISS HIT | HIT MISS | HIT HIT

# 直播特殊验证项
# 拉流-合并回源 | 推流 | 转推 | 转码

#=======================================================================================
# 功能：捕获 Ctrl + C 将后台进程全部终止
# 入参：bg_pids, progress_pid
# 出参：None
function onCtrlC () {
    exec 3>&2          # 3 is now a copy of 2
    exec 2> /dev/null  # 2 now points to /dev/null
    kill ${bg_pids} ${progress_pid} >/dev/null 2>&1
    sleep 1            # sleep to wait for process to die
    exec 2>&3          # restore stderr to saved
    exec 3>&-          # close saved version
    echo -e "${c_bir}IDS!\n${c_e}"
    echo -e "${c_bir}[IDS-100] Ctrl+C is captured, exiting...\n${c_e}"
    exit 100
}

#=======================================================================================
# 功能：捕获 `exit` 退出指令，并计算脚本实际运行时间
# 入参：TS
# 出参：None
function onExit () {
    local te=`date +%s`
    echo -e "${c_bib}Start Time: $(date -d@$((ts-0)) +'%Y-%m-%d %H:%M:%S')"
    echo -e "${c_bib}End Time  : `date +'%Y-%m-%d %H:%M:%S'`"
    echo -e "${c_bib}Duration  : $((te-ts)) seconds\n${c_e}"
}

#=======================================================================================
# 功能：检查输入的时间范围是否符合格式要求：14天内，不能是未来时间，10位数字
# 入参：time_range
# 出参：current, year, month, day, hour, time_range
function time_check() {
    # 如果入参 time_range 的值是空，或者说函数没有入参
    if [[ $time_range == '' ]]; then
        time_range=`date +%Y%m%d%H`
        year=${time_range:0:4}
        month=${time_range:4:2}
        day=${time_range:6:2}
        hour=${time_range:8:2}
        current='yes'
        return 0
    fi

    # 检查入参是否正确：长度，表示的时间范围等
    [[ ! $time_range =~ ^[0-9]{10}$ ]] && { echo -e "${c_br}[IDS-101] 请输入正确的时间格式，退出...\n${c_e}"; exit 101; }
    # 验证入参是10天以内的时间范围
    now=`date +%s`
    # 准备工作，后续要用
    year=${time_range:0:4}
    month=${time_range:4:2}
    day=${time_range:6:2}
    hour=${time_range:8:2}
    # 将入参转换为秒
    previous=`date -d "$year-$month-$day $hour:00:00" +"%s"`
    # 计算当前时间 - 入参时间
    let range_s=now-previous
    let range_d=range_s/86400
    # 如果是14天以外的入参时间，则不可查
    [[ $range_d -gt 10 ]] && { echo -e "${c_br}[IDS-102] 只能查找最近10天以内的日志记录，退出...\n${c_e}"; exit 102; }
    # 判断 time_range 是否是当前时间，并用 current 来标识，默认是当前，即 current = yes
    [[ $time_range == `date +%Y%m%d%H` ]] && current='yes' || current='no'
}

#=======================================================================================
# 功能：等待后台进程结束时，输出进度条
# 入参：bg_pids, level
# 出参：None
function progress() {
    length=75
    ratio=1
    # ps -p pidlist命令的作用是列出pidlist里面所有pid的运行状态，已经结束的pid将不会被列出，每个pid一行
    while [[ "$(ps -p ${bg_pids} | wc -l)" -ne 1 ]]; do
        mark='>'
        progress_bar=''
        # 小于ratio的部分填充'>'，大于ratio的部分，填充' '，必须是空格，不然ratio重新变成1的时候，没有变化
        for i in $(seq 1 $length); do
            if [[ $i -gt $ratio ]]; then
                mark=' '
            fi
            progress_bar="${progress_bar}${mark}"
        done
        echo -ne "${c_bic}Collecting $level Data: ${progress_bar}\r${c_e}"
        ratio=$((ratio+1))
        if [[ $ratio -gt $length ]]; then
            ratio=1
        fi
        sleep 1
    done
}

#=======================================================================================
# 功能：适用于旧版本的reqID，获取$label_en和$time_range
# 入参：None
# 出参：time_range, label_en, edge
function node_time_old() {
    echo -e "${c_bg}请输入request ID所在节点信息，支持边缘RIP/VIP/中英文节点名：${c_e}"
    # 60s时间接收输入：要查询的节点
    read -t 60 edge
    # 判断60s内无输入，则自动退出
    [[ $? -ne 0 ]] && { echo -e "${c_br}[IDS-103] 60s内无任何输入，退出...\n${c_e}"; exit 103; }
    # 判断输入的边缘节点信息是空，则自动退出
    [[ $edge == '' ]] && { echo -e "${c_br}[IDS-104] 请输入正确的边缘节点信息，退出...\n${c_e}"; exit 104; }

    # 判断边缘节点信息是否在天翼平台
    ips $edge > ips.log 2>&1
    [[ $? -ne 0 ]] && { cat ips.log; echo -e "${c_br}[IDS-105]${c_e}"; exit 105; } || cd $trash
    label_en=`cat ips.log | grep -Eo '(ct|cu|cm|bgp|ctbgp|cmbgp|cubgp|as|eu|sa|na|cbn|cern)_[a-z]{2,3}_[a-z]{2,20}[0-9]{1,2}_(c|e|n)[0-9]{0,2}' | sort | uniq`
    # 输入中文节点名的情况下，可能会得到两个边缘节点，需要手动确认是哪个
	number=`echo $label_en | awk '{print NF}'`
    [[ $label_en == '' ]] && { echo -e "${c_br}[IDS-106] 请确认输入的 $edge 是边缘节点，退出...\n${c_e}"; exit 106;  }
    if [[ $number -gt 1 ]]; then
        echo $label_en | awk '{for(i=1;i<=NF;i++) print "  -", $i}'
        echo -e "${c_bp}输入的 $edge 边缘节点中有两个组，请确认具体是哪个：${c_e}"
        read -t 60 label_en_input
        # 判断60s内无输入，则自动退出
        [[ $? -ne 0 ]] && { echo -e "${c_br}[IDS-107] 60s内无任何输入，退出...\n${c_e}"; exit 107; }
        # 判断输入信息是否是正确的
        echo $label_en | grep -wq $label_en_input
        [[ $? -ne 0 ]] && { echo -e "${c_br}[IDS-108] 需要从如上选择正确的边缘节点信息，请重新运行，退出...\n${c_e}"; exit 108; }
        label_en=$label_en_input
    fi

    # 60s时间接收输入：要查询的时间
    echo -e "${c_bg}请输入要查询的reqID生成时间，格式为yyyymmddHH(默认当前 - $(date +%Y%m%d%H)): ${c_e} "
    read -t 60 time_range
    [[ $? -ne 0 ]] && { echo -e "${c_br}[IDS-109] 60s内无任何输入，退出...\n${c_e}"; exit 109; }
}


#=======================================================================================
# 功能：适用于新版本的reqID，获取label_en和time_range
# 入参：None
# 出参：time_range, label_en, edge
function node_time_new() {
    ts_hex=`echo $req_id | awk -F '_' '{print $1}'`
    ts_hex=`printf "%d" "0x$ts_hex"`
    time_range=`date -d @$ts_hex +'%Y%m%d%H'`
    ts=`date -d @$ts_hex +'%Y-%m-%d %H:%M:%S'`

    # 根据中间部分的信息找出label_en
    edge=`echo $req_id | awk -F '_' '{print $2}'`
    edge=`echo $edge | awk -F '-' '{print $1"-"$2"-ca"$3}'`
    ping -c 4 -q $edge > ping.log
    if [[ $? -eq 0 ]]; then
        rip=`cat ping.log | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}"`
        # 获取$label_en
        ips $rip > ips.log 2>&1
        [[ $? -ne 0 ]] && { cat ips.log; echo -e "${c_br}[IDS-110]${c_e}"; exit 110; } || cd $trash
        label_en=`cat ips.log | grep -Eo '(ct|cu|cm|bgp|ctbgp|cmbgp|cubgp|as|eu|sa|na|cbn|cern)_[a-z]{2,3}_[a-z]{2,20}[0-9]{1,2}_(e)[0-9]{0,2}' | sort | uniq`
    else
        echo -e "${c_br}[IDS-111] 本地到边缘缓存主机$edge的网络无法ping通，需要检查下服务器是否宕机，或者检查下${edge}是否是天翼的节点...${c_e}"
        exit 111
    fi
    echo -e "${c_bib}将在$rip($label_en)上查找$ts时间点的日志，如果时间靠近${c_bir}整点${c_bib}，有可能会因日志切割导致无法正确查询...${c_e}"
}

#=======================================================================================
# 功能：获取 prod_type - 产品类型
# 入参：None
# 出参：TS, prod_type
function prod_type_inp() {
    # 60s时间接收输入：要查询的类型
    echo -e "1. 静态/下载/点播/全站(default - v03/ov06)"
    echo -e "2. 直播(ACC1/2/3)"
    echo -e "3. 安全"
    echo -e "4. quic"
    echo -e "5. L1/L2/L3/L4/L5/L6/L7(e.g. 查询L3，则输入5.3)"
    echo -e "${c_bg}请输入要查询的业务类型(目前仅支持1/2/5)： ${c_e}"
    read -t 60 prod_type
    [[ $? -ne 0 ]] && { echo -e "${c_br}[IDS-112] 60s内无任何输入，退出...\n${c_e}"; exit 112; }
    [[ $prod_type == '' ]] && prod_type='1'
    ts=`date +%s`       # 开始计时
}

#=======================================================================================
# 功能：CDN的NG访问日志处理，静态/点播/下载/全站/L1-7
# 入参：rip_list, bg_pids, current, year, month, day, hour, req_id
# 出参：access_$rip
function cdn_log_access() {
    # 根据业务类型，指定前缀
    [[ $prod_type == '1' ]] && prefix=''
    [[ $prod_type == '5.1' ]] && prefix='L1_'
    [[ $prod_type == '5.2' ]] && prefix='L2_'
    [[ $prod_type == '5.3' ]] && prefix='L3_'
    [[ $prod_type == '5.4' ]] && prefix='L4_'
    [[ $prod_type == '5.5' ]] && prefix='L5_'
    [[ $prod_type == '5.6' ]] && prefix='L6_'
    [[ $prod_type == '5.7' ]] && prefix='L7_'
    # 如果time_range是当前时间
    if [[ $current == 'yes' ]]; then
        # ssh 进每一个rip，搜索core_access.log，设定ssh连接超时时长为CT，*${time_range}*表示包含time_range就要进行搜索
        # 把搜索的结果放进access_$rip文件，所有的ssh命令都后台执行，并将它们的PID存入bg_pids
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $cdn_access_log/${prefix}core_access.log | grep $req_id;
            cat $cdn_access_log/${prefix}core_access.log_*${time_range}* | grep $req_id" > access_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    # 如果time_range不是当前时间
    else
        # 简单粗暴地，分别过滤回滚文件和未归档两部分日志文件
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $cdn_access_log/${prefix}core_access.log_*${time_range}* | grep $req_id;
            zcat $cdn_access_log/$year$month$day/${prefix}core_access.log_*${time_range}* | grep $req_id" > access_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    fi
}

#=======================================================================================
# 功能：CDN的ATS回源日志处理，静态/点播/下载/全站/L1-7
# 入参：rip_list, bg_pids, current, year, month, day, hour, req_id
# 出参：origin_$rip
function cdn_log_origin() {
    # 如果time_range是当前时间
    if [[ $current == 'yes' ]]; then
        # ssh 进每一个rip，搜索origin.log，设定ssh连接超时时长为CT
        # 把搜索的结果放进origin_$rip文件，所有的ssh命令都后台执行，并将它们的PID存入bg_pids
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $cdn_origin_log/origin.log | grep $req_id;
            cat $cdn_origin_log/origin.log_*${time_range}* | grep $req_id" > origin_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    # 如果time_range不是当前时间
    else
        # 简单粗暴地，分别过滤回滚文件和未归档两部分日志文件
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $cdn_origin_log/origin.log_*${time_range}* | grep $req_id;
            zcat $cdn_origin_log/$year$month$day/origin.log_*${time_range}* | grep $req_id" > origin_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    fi
}

#=======================================================================================
# 功能：搜索处理每一层级的CDN日志，静态/点播/下载/全站/L1-7
# 入参：level, label_en, current, year, month, day, hour
# 出参：upstream, port, upstream_and_port
function cdn_log_proc() {
    # -------------------------------------------------------------------------
    # 获取 rip 列表，并初始化 bg_pids 数组
    rip_list=`cat $data/ip.group | grep $label_en | awk '{print $1}' | sort | uniq`
    bg_pids=''
    # access日志：/home/log/cluster_gateway_log/core_access.log
    cdn_log_access "$rip_list" "$bg_pids" "$current" "$year" "$month" "$day" "$hour" "$req_id"
    # origin日志：/home/log/trafficserver/origin.log
    cdn_log_origin "$rip_list" "$bg_pids" "$current" "$year" "$month" "$day" "$hour" "$req_id"

    # 动态进度条
    progress "${bg_pids}" $level &
    progress_pid=$(jobs -p | tail -1)

    # 等待所有 bg_pids 存储的后台进程执行完毕
    wait
    echo -ne "${c_bic}$level Data collected.${c_e}"
    echo -ne "                                                                                "

    # -------------------------------------------------------------------------
    # 处理上述生成的rip中拿到的access和origin日志，筛选出包含req_id的行
    # 初始化access.log和origin.log -- 处理单个rip上生成的access/origin日志，匹配req_id
    # 如果找到则将对应的rip和日志内容用追加到相应日志尾部，以"为分隔，rip作为日志的最后一个字段
    log_merge $rip_list $req_id

    # -------------------------------------------------------------------------
    # 可以根据当前文件夹下的图片cdnlog_search_logic.jpg来理解
    # 如果找到了access日志，则过滤打印输出，并将access_flg重置
    if [[ $access_flg -eq 1 ]]; then
        # v03版本第5个字段是日志打印时间戳
        cat access.log | sort -t'"' -n -k5 > log && mv log access.log
        # 按时间排序，打印找到的所有日志内容
        cat access.log | while read line; do
            access_ip=`echo $line | awk -F'"' '{print $NF}'`
            echo -e "${c_by}\n$level NG Log: searching $label_en ...... $access_ip${c_e}"
            # 将匹配"*的右边最短路径删除，也就是将之前追加到最末尾的rip删掉
            out=${line%\"*}
            log_format "$out"
        done
        access_flg=0
        # 如果找到了origin日志，则过滤打印输出，并将origin_flg重置
        if [[ $origin_flg -eq 1 ]]; then
            # ov06版本第43个字段是回源开始时间戳
            cat origin.log | sort -t'"' -n -k43 > log && mv log origin.log
            cat origin.log | while read line; do
                origin_ip=`echo $line | awk -F'"' '{print $NF}'`
                echo -e "${c_by}\n$level ATS Log: searching $label_en ...... $origin_ip${c_e}"
                # 将匹配"*的右边最短路径删除，也就是将之前追加到最末尾的rip删掉
                out=${line%\"*}
                log_format "$out"
            done
            origin_flg=0
            # 如果origin日志找到了，则从origin日志中获取upstream和port
            upstream_and_port=`cat origin.log | awk -F '"' '{print $8":"$9}' | sort | uniq`
            uap=$upstream_and_port
            for nhi in $upstream_and_port; do
                upstream=`echo $nhi | awk -F ':' '{print $1}'`
                port=`echo $nhi | awk -F ':' '{print $2}'`
                ips $upstream > ips.log 2>&1
                # 如果upstream是天翼的IP，则输出如下
                if [[ $? -eq 0 ]]; then
                    echo -e "${c_bp}\n[ATS-$level]: 从origin日志查询到$req_id的回上层地址是$nhi(CTC IP)，继续查询...${c_e}"
                    cd $trash
                # 如果upstream不是天翼的IP，则输出如下
                else
                    echo -e "${c_br}\n[ATS-$level]: 从origin日志查询到$req_id的回源地址$nhi不是天翼的IP，可能是ATS回源了${c_e}"
                    echo -e "${c_bp}请确认${c_bc}$nhi${c_bp}是否是源站IP。\n${c_e}"
                    uap=`echo $uap | sed -n "s/$nhi//p"`
                fi
            done
            [[ $uap == '' ]] && { echo -e "${c_bg}[IDS-113] 所有IP均已处理完毕，退出...${c_e}"; exit 113; } || { upstream_and_port=$uap; cd $trash; }
        # 如果没有找到origin日志，则从access日志获取upstream和port，并用ips脚本检测查询upstream是否是天翼的IP
        else
            # 如果没有找到origin日志，则判断是否缓存了
            # MISS from hb-wuhan13-ca26, MISS from fj-quanzhou6-ca16 -- ct_fj_quanzhou6_e1
            hit=`echo $label_en | awk -F'_' '{print "HIT from", $2"-"$3}'`
            cat access.log | grep -iq "$hit"
            [[ $? -eq 0 ]] && { echo -e "${c_bp}\n[IDS-114] [Access-$level]: 边缘已经命中缓存，搜索结束...${c_e}\n"; exit 114; }

            # 如果不是缓存住了，从access日志获取回上层信息
            uap_28=`cat access.log | tail -1 | awk -F '"' '{print $28}'`
            uap_24=`cat access.log | tail -1 | awk -F '"' '{print $24}'`
            if [[ $uap_28 == '' || $uap_28 == '-' ]]; then
                if [[ $uap_24 == '' || $uap_24 == '-' ]]; then
                    echo -e "${c_br}\n[IDS-115] [Access-$level]: 没有找到origin日志，也找不到$req_id的回上层地址，无法继续查询，退出...${c_e}"
                    exit 115
                else
                    upstream_and_port=$uap_24
                fi
            else
                upstream_and_port=$uap_28
            fi

            # WARNING: 此处没有考虑当uap_24有多个后端代理的情况
            # 判断是否是天翼云 IP
            upstream_and_port=`echo $upstream_and_port | tr ',' ' '`
            uap=$upstream_and_port
            for nhi in $upstream_and_port; do
                upstream=`echo $nhi | awk -F ':' '{print $1}'`
                port=`echo $nhi | awk -F ':' '{print $2}'`
                ips $upstream > ips.log 2>&1
                # 如果upstream是天翼的IP，则输出如下
                if [[ $? -eq 0 ]]; then
                    echo -e "${c_bp}\n[NG-$level]: 从access日志查询到$req_id的回上层地址是$nhi(CTC IP)，继续查询...${c_e}"
                    cd $trash
                # 如果upstream不是天翼的IP，则输出如下
                else
                    echo -ne "${c_br}\n[NG-$level]: 从access日志查询到$req_id的回源地址$nhi不是天翼的IP，可能是NG直接回源了。${c_e}"
                    echo -e "${c_br}或者查询access主机上的error日志，看是否是ats因自身或源站故障导致无法应答${c_e}"
                    echo -e "${c_bp}请确认${c_bc}$nhi${c_bp}是否是源站IP。${c_e}"
                    uap=`echo $uap | sed -n "s/$nhi//p"`
                fi
            done
            [[ $uap == '' ]] && { echo -e "${c_bg}[IDS-116] 所有IP均已处理完毕，退出...${c_e}"; exit 116; } || { upstream_and_port=$uap; cd $trash; }
        fi
    # 如果没有找到access日志，则要判断当前是在哪个层级，边缘-一层父-二层父
    else
        # 如果是一层父或者是二层父，再找一下origin日志是否存在
        if [[ $level == 'Center' || $level == 'Nation' ]]; then
            # 如果origin日志有找到记录，则打印出来
            if [[ $origin_flg -eq 1 ]]; then
                cat origin.log | sort -t'"' -n -k43 > log && mv log origin.log
                cat origin.log | while read line; do
                    origin_ip=`echo $line | awk -F'"' '{print $NF}'`
                    echo -e "${c_by}\n$level ATS Log: searching $label_en ...... $origin_ip${c_e}"
                    out=${line%\"*}
                    log_format "$out"
                done
                origin_flg=0
                # 如果origin日志找到了，则从origin日志中获取upstream和port
                upstream_and_port=`cat origin.log | awk -F '"' '{print $8":"$9}' | sort | uniq`
                uap=$upstream_and_port
                for nhi in $upstream_and_port; do
                    upstream=`echo $nhi | awk -F ':' '{print $1}'`
                    port=`echo $nhi | awk -F ':' '{print $2}'`
                    ips $upstream > ips.log 2>&1
                    # 如果upstream是天翼的IP，则输出如下
                    if [[ $? -eq 0 ]]; then
                        echo -e "${c_bp}\n[ATS-$level]: 从origin日志查询到$req_id的回上层地址是$nhi(CTC IP)，继续查询...${c_e}"
                        cd $trash
                    # 如果upstream不是天翼的IP，则输出如下
                    else
                        echo -e "${c_br}\n[ATS-$level]: 从origin日志查询到$req_id的回源地址$nhi不是天翼的IP，可能是ATS回源了${c_e}"
                        echo -e "${c_bp}请确认${c_bc}$nhi${c_bp}是否是源站IP。\n${c_e}"
                        uap=`echo $uap | sed -n "s/$nhi//p"`
                    fi
                done
                [[ $uap == '' ]] && { echo -e "${c_bg}[IDS-117] 所有IP均已处理完毕，退出...${c_e}"; exit 117; } || { upstream_and_port=$uap; cd $trash; }
            # 如果origin日志也有找到记录
            else
                echo -e "${c_br}\n[IDS-118] [NG-ATS-$level]: 很奇怪，从上层NG/ATS日志查询到$req_id的回源地址是$upstream_and_port(CTC IP)，却找不到access/origin日志。可以检查一下父层对应日志是否已经被删除，或者查询的日志时间是否是靠近整点，例如22:59:01，由于日志切割机制，基于本工具的逻辑，这样的日志有可能会漏掉，如是这种情况，请更换访问日志，重新查询。\n${c_e}"
                exit 118
            fi
        # 如果边缘机器上就找不到 NG 日志
        else
            echo -e "\n${c_br}[IDS-119] [NG-$level]: 无法在$label_en($edge)上找到$req_id，请确认输入的request ID，访问时间或者节点信息是准确的。\n${c_e}"
            exit 119
        fi
    fi
}

#=======================================================================================
# 功能：分层级查询日志链
# 入参：level, cdn_log_proc
# 出参：None
function cdn_log_search() {
    # 1. 搜索边缘日志
    level="Edge" && cdn_log_proc $level
    # 脚本运行到此处，说明边缘access或者origin日志已经找到，upstream_and_port也已经获得且一定是天翼IP
    for nhi_e in $upstream_and_port; do
        upstream=`echo $nhi_e | awk -F ':' '{print $1}'`
        port=`echo $nhi_e | awk -F ':' '{print $2}'`
        ips $upstream > ips.log 2>&1 && cd $trash
        # 获取下一层级的 label_en，这里依赖ips工具输出的格式
        label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
        width=`tput cols` && echo
        for i in `seq $width`; do
            echo -ne "${c_bb}=${c_e}"
        done
        echo -e "${c_bb}\nNext Hop IP: $nhi_e\n${c_e}"

        # 2. 搜索一层父日志
        level="Center" && cdn_log_proc $level
        # 脚本运行到此处，说明边缘access或者origin日志已经找到，upstream_and_port也已经获得且一定是天翼IP
        for nhi_c in $upstream_and_port; do
            upstream=`echo $nhi_c | awk -F ':' '{print $1}'`
            port=`echo $nhi_c | awk -F ':' '{print $2}'`
            ips $upstream > ips.log 2>&1 && cd $trash
            # 获取下一层级的 label_en，这里依赖ips工具输出的格式
            label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
            width=`tput cols` && echo
            for i in `seq $width`; do
                echo -ne "${c_bb}=${c_e}"
            done
            echo -e "${c_bb}\nNext Hop IP: $nhi_c\n${c_e}"

            # 3. 搜索二层父日志
            level="Nation" && cdn_log_proc $level
            # 脚本运行到此处，说明边缘access或者origin日志已经找到，upstream_and_port也已经获得且一定是天翼IP
            for nhi_n in $upstream_and_port; do
                upstream=`echo $nhi_n | awk -F ':' '{print $1}'`
                port=`echo $nhi_n | awk -F ':' '{print $2}'`
                ips $upstream > ips.log 2>&1 && cd $trash
                # 获取下一层级的 label_en，这里依赖ips工具输出的格式
                label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
                width=`tput cols` && echo
                for i in `seq $width`; do
                    echo -ne "${c_bb}=${c_e}"
                done
                echo -e "${c_bb}\nNext Hop IP: $nhi_n\n${c_e}"

                # 4. 未知层级 -- 容错作用
                level="Unknown" && cdn_log_proc $level
                # 脚本运行到此处，说明边缘access或者origin日志已经找到，upstream_and_port也已经获得且一定是天翼IP
                for nhi_u in $upstream_and_port; do
                    upstream=`echo $nhi_u | awk -F ':' '{print $1}'`
                    port=`echo $nhi_u | awk -F ':' '{print $2}'`
                    ips $upstream > ips.log 2>&1 && cd $trash
                    # 获取下一层级的 label_en，这里依赖ips工具输出的格式
                    label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
                    width=`tput cols` && echo
                    for i in `seq $width`; do
                        echo -ne "${c_bb}=${c_e}"
                    done
                    echo -e "${c_bb}\nNext Hop IP: $nhi_u\n${c_e}"
                done
            done
        done
    done
}

#=======================================================================================
# 功能：直播业务访问日志处理 -- all: flv, hls, rtmp
# 入参：rip_list, bg_pids, current, year, month, day, hour, req_id, rbplus
# 出参：access_$rip
function live_log_access() {
    # 如果time_range是当前时间，则搜索当前文件夹下的对应日志，以及回滚日志
    if [[ $current == 'yes' ]]; then
        # ssh 进每一个rip，搜索uni_access.log和uni_access.log_${time_range}*，设定ssh连接超时时长 CT
        # 把搜索的结果放进access_$rip文件，所有的ssh命令都后台执行，并将它们的PID存入$bg_pids
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $livelog/uni_access.log* | grep $req_id" > access_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    # 如果time_range不是当前时间，则搜索当前文件夹下的回滚日志，以及目录下的归档日志
    else
        # 简单粗暴地，分别过滤回滚文件和未归档两部分日志文件，因为归档文件是奇数小时命名，所以过滤
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $livelog/uni_access.log* | grep $req_id;
            zcat $livelog/$dirpath/uni_access.log_${rbplus}* | grep $req_id;
            zcat $livelog/$dirpath/uni_access.log_${rb}* | grep $req_id;
            zcat $livelog/$dirpath/uni_access.log_${rbminus}* | grep $req_id" > access_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    fi
}


#=======================================================================================
# 功能：直播业务回源日志处理 -- flv/hls
# 入参：rip_list, bg_pids, current, year, month, day, hour, req_id, rbplus
# 出参：origin_$rip
function live_log_origin() {
    # 如果time_range是当前时间，则搜索当前文件夹下的对应日志，以及回滚日志
    if [[ $current == 'yes' ]]; then
        # ssh进每一个 rip，搜索uni_origin.log和uni_origin.log_${time_range}*，设定ssh连接超时时长CT
        # 把搜索的结果放进origin_$rip/ats_$rip文件，所有的ssh命令都后台执行，并将它们的PID存入$bg_pids
        # 因为hls业务走的是点播逻辑，是从ATS回源的，所以为了后续方便处理，直接两种情况都获取一下，也不会多用多少时间
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $livelog/uni_origin.log* | grep $req_id" > origin_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
            # for hls
            ssh -o ConnectTimeout=$CT $rip "
            cat $liveatslog/origin.log* | grep $req_id" > ats_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    # 如果time_range不是当前时间，则搜索当前文件夹下的回滚日志，以及目录下的归档日志
    else
        # 简单粗暴地，分别过滤回滚文件和未归档两部分日志文件，因为归档文件是奇数小时命名，所以过滤
        for rip in $rip_list; do
            ssh -o ConnectTimeout=$CT $rip "
            cat $livelog/uni_origin.log* | grep $req_id;
            zcat $livelog/$dirpath/uni_origin.log_${rbplus}* | grep $req_id;
            zcat $livelog/$dirpath/uni_origin.log_${rb}* | grep $req_id;
            zcat $livelog/$dirpath/uni_origin.log_${rbminus}* | grep $req_id" > origin_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
            # for hls
            ssh -o ConnectTimeout=$CT $rip "
            cat $liveatslog/origin.log* | grep $req_id;
            zcat $liveatslog/$dirpath_ats/origin.log_${rb_ats_minus}* | grep $req_id
            zcat $liveatslog/$dirpath_ats/origin.log_${rb_ats}* | grep $req_id
            zcat $liveatslog/$dirpath_ats/origin.log_${rb_ats_plus}* | grep $req_id" > ats_${rip} 2>&1 &
            bg_pids=$bg_pids' '$(jobs -p | tail -1)
        done
    fi
}

#=======================================================================================
# 功能：区分直播日志格式，因为不同格式上层IP以及HIT/MISS字段位置不同，内部回源IP&端口位置也不同
# 功能：判断是否是hls业务 | flv业务的回上层IP&端口 | hls业务的回上层IP&端口
# 入参：--ishls, --uapflv, --uaphls, access.log, origin.log
# 出参：HOM, log_v, upstream, upstream_and_port, port, ishls
function log_version() {
    opt=$1
    log=$2
    log_v=`tail -1 $log | awk -F '"' '{print $1}'`
    log_v=${log_v:${#log_v}-1:1}
    # 根据不同的日志版本，从uni_access日志种获取回源端口，进而判断是hls业务还是其他
    if [[ $opt == '--ishls' ]]; then
        if [[ $log_v == '1' ]]; then
            upstream=`tail -1 $log | awk -F '"' '{print $30}' | awk -F':' '{print $1}'`
            port=`tail -1 $log | awk -F '"' '{print $30}' | awk -F':' '{print $2}'`
        elif [[ $log_v == '2' ]]; then
            upstream=`tail -1 $log | awk -F '"' '{print $32}' | awk -F':' '{print $1}'`
            port=`tail -1 $log | awk -F '"' '{print $32}' | awk -F':' '{print $2}'`
        elif [[ $log_v == '3' ]]; then
            upstream=`tail -1 $log | awk -F '"' '{print $7}'`
            port=`tail -1 $log | awk -F '"' '{print $8}'`
        else
            echo -e "\n${c_br}[IDS-120] 未知的日志格式，请联系fanmf11@chinatelecom.cn反馈...${c_e}"
            exit 120
        fi
        [[ $port == '8080' ]] && ishls=1 || ishls=0

    # 如果是flv业务，根据不同的日志版本，从uni_origin日志中获取回上层IP以及端口
    elif [[ $opt == '--uapflv' ]]; then
        if [[ $log_v == '1' ]]; then
            upstream=`tail -1 $log | awk -F '"' '{print $8}' | awk -F':' '{print $1}'`
            port=`tail -1 $log | awk -F '"' '{print $8}' | awk -F':' '{print $2}'`
            HOM=`tail -1 $log | awk -F '"' '{print $29}'`
        elif [[ $log_v == '2' ]]; then
            upstream=`tail -1 $log | awk -F '"' '{print $8}'`
            port=`tail -1 $log | awk -F '"' '{print $9}'`
            HOM=`tail -1 $log | awk -F '"' '{print $31}'`
        elif [[ $log_v == '3' ]]; then
            upstream=`tail -1 $log | awk -F '"' '{print $10}'`
            port=`tail -1 $log | awk -F '"' '{print $11}'`
            HOM=`tail -1 $log | awk -F '"' '{print $29}'`
        else
            echo -e "\n${c_br}[IDS-121] 未知的日志格式，请联系fanmf11@chinatelecom.cn反馈...${c_e}"
            exit 121
        fi
        if [[ $HOM == 'HIT' ]]; then
            echo -e "\n${c_big}[IDS-122] $level $log Log: 请求在该层命中，退出...${c_e}"
            exit 122
        fi
    # 如果是hls业务，日志是ov06版本的，回上层IP以及端口是固定位置
    elif [[ $opt == '--uaphls' ]]; then
        upstream=`cat origin.log | tail -1 | awk -F '"' '{print $8}'`
        port=`cat origin.log | tail -1 | awk -F '"' '{print $9}'`
        upstream_and_port=${upstream}":"${port}
    fi
}

#=======================================================================================
# 功能：处理CDN，直播的access和origin日志
# 入参：access_$rip, origin_$rip, rip_list, req_id
# 出参：access.log, origin.log
function log_merge() {
    # 处理上述生成的rip中拿到的access和origin日志，筛选出包含req_id的行
    # 初始化access.log和origin.log -- 处理单个rip上生成的access/origin日志，匹配req_id
    # 如果找到则将对应的rip和日志内容用追加到相应日志尾部，以"为分隔，rip作为日志的最后一个字段
    > access.log && > origin.log
    for rip in $rip_list; do
        cat access_$rip | grep -q $req_id
        # access和origin日志的处理方式一致，由于一个访问，可能在边缘机器上出现多次 -- 合并回源
        # 所以要检查每一个rip的日志，凡是匹配到的都记录到access.log和origin.log
        if [[ $? -eq 0 ]]; then
            access_flg=1
            cat access_$rip | grep $req_id > log
            cat log | while read line; do
                echo -n $line >> access.log
                echo '"'$rip >> access.log
            done
        fi
        cat origin_$rip | grep -q $req_id
        if [[ $? -eq 0 ]]; then
            origin_flg=1
            cat origin_$rip | grep $req_id > log
            cat log | while read line; do
                echo -n $line >> origin.log
                echo '"'$rip >> origin.log
            done
        fi

        # 检查是否有 ssh 连接失败的情况，有的话，打印出来
        cat access.log | grep -iq 'Connection timed out during banner exchange'
        [[ $? -eq 0 ]] && { echo -e "${c_br}[NG-$level] SSH Connection Failed:${c_e}"; echo $rip; }

        cat origin.log | grep -iq 'Connection timed out during banner exchange'
        [[ $? -eq 0 ]] && { echo -e "${c_br}[ATS-$level] SSH Connection Failed:${c_e}"; echo $rip; }
    done

}

#=======================================================================================
# 功能：获取直播网关日志，搜索处理每一层级的live日志
# 入参：level, label_en, current, year, month, day, hour
# 出参：upstream, upstream_port
function live_log_proc() {
    # 获取 rip 列表，并初始化 bg_pids 数组
    rip_list=`cat $data/ip.group | grep $label_en | awk '{print $1}' | sort | uniq`
    bg_pids=''
    dirpath="$year-$month-$day"
    dirpath_ats="$year$month$day"
    rb="$year-$month-$day-$hour"
    rb_ats="$year$month$day$hour"
    # 直播归档日志规则：偶数小时的日志归档到相邻较大的奇数小时命名的日志中
    # 所以如果是过去时间，hour 是偶数，则加一，如果是奇数，则保持
    # 这里奇数的时候设置成"KEEP"字符串，为的是简化搜索逻辑，避免更多的判断分支
    let clock=`echo "obase=10; $hour" | bc`
    hourplus=`printf "%02d" $((clock+1))`
    hourminus=`printf "%02d" $((clock-1))`
    # [[ $((clock%2)) -eq 1 ]] && rbplus=$rb || rbplus="$year-$month-$day-$hourplus"
    rbplus="$year-$month-$day-$hourplus"
    rbminus="$year-$month-$day-$hourminus"
    rb_ats_minus="$year$month$day$hourminus"
    rb_ats_plus="$year$month$day$hourplus"

    # access日志：/home/log/cluster_live_log/uni_access.log
    live_log_access "$rip_list" "$bg_pids" "$current" "$year" "$month" "$day" "$hour" "$req_id" $rbplus
    # origin日志：/home/log/cluster_live_log/uni_origin.log
    live_log_origin "$rip_list" "$bg_pids" "$current" "$year" "$month" "$day" "$hour" "$req_id" $rbplus

    # 动态进度条
    progress "${bg_pids}" $level &
    progress_pid=$(jobs -p | tail -1)

    # 等待所有 bg_pids 存储的后台进程执行完毕
    wait
    echo -ne "${c_bic}$level Data collected.${c_e}"
    echo -ne "                                                                                "

    # -------------------------------------------------------------------------
    # 处理上述生成的rip中拿到的access和origin日志，筛选出包含req_id的行
    # 初始化access.log和origin.log -- 处理单个rip上生成的access/origin日志，匹配req_id
    # 如果找到则将对应的rip和日志内容用追加到相应日志尾部，以"为分隔，rip作为日志的最后一个字段
    log_merge $rip_list $req_id

    # 判断是否是hls或者ts业务
    if [[ $access_flg -eq 1 ]]; then
        cat access.log | sort -t'"' -n -k2 -k6 > log && mv log access.log
        log_version --ishls access.log
        if [[ $ishls -eq 1 ]]; then
            for rip in $rip_list; do
                cp ats_$rip origin_$rip
            done
            # 重新做一次日志整理
            log_merge $rip_list $req_id
        fi
    fi

    # 如果是hls或者ts业务
    if [[ $ishls -eq 1 ]]; then
        # 如果找到了access日志，则过滤打印输出，并将access_flg重置
        if [[ $access_flg -eq 1 ]]; then
            cat access.log | sort -t'"' -n -k2 -k6 > log && mv log access.log
            # 按时间排序，打印找到的所有日志内容
            cat access.log | while read line; do
                access_ip=`echo $line | awk -F'"' '{print $NF}'`
                echo -e "${c_big}\n$level Access Log:${c_by} searching $label_en ...... $access_ip${c_e}"
                out=${line%\"*}
                log_format "$out"
            done
            access_flg=0
            log_version --uapflv access.log
            # 找到了origin日志，则过滤打印输出，并将origin_flg重置
            if [[ $origin_flg -eq 1 ]]; then
                cat origin.log | sort -t'"' -n -k43 > log && mv log origin.log
                cat origin.log | while read line; do
                    origin_ip=`echo $line | awk -F'"' '{print $NF}'`
                    echo -e "${c_big}\n$level Origin Log:${c_by} searching $label_en ...... $origin_ip${c_e}"
                    out=${line%\"*}
                    log_format "$out"
                done
                origin_flg=0
                log_version --uaphls origin.log
            # 没有找到origin日志，
            else
                echo -e "\n${c_br}[IDS-123] [Origin-$level]: 很奇怪，Access未命中，也无法在$label_en上找到$req_id，请联系fanmf11@chinatelecom.cn排查未知的场景。\n${c_e}"
                exit 123
            fi
        # 如果边缘Access没有找到request id对应的日志信息
        else
            echo -e "\n${c_br}[IDS-124] [ACCESS-$level]: 无法在$label_en($edge)上找到$req_id，请确认输入的request ID，访问时间或者节点信息是准确的。\n${c_e}"
            exit 124
        fi
    # 如果不是hls，而是rtmp或者flv业务
    else
        # 如果找到了access日志，则过滤打印输出，并将access_flg重置
        if [[ $access_flg -eq 1 ]]; then
            cat access.log | sort -t'"' -n -k2 -k6 > log && mv log access.log
            # 按时间排序，打印找到的所有日志内容
            cat access.log | while read line; do
                access_ip=`echo $line | awk -F'"' '{print $NF}'`
                echo -e "${c_big}\n$level Access Log:${c_by} searching $label_en ...... $access_ip${c_e}"
                out=${line%\"*}
                log_format "$out"
            done
            access_flg=0
            log_version --uapflv access.log
            # 找到了origin日志，则过滤打印输出，并将origin_flg重置
            if [[ $origin_flg -eq 1 ]]; then
                cat origin.log | sort -t'"' -n -k2 -k6 > log && mv log origin.log
                cat origin.log | while read line; do
                    origin_ip=`echo $line | awk -F'"' '{print $NF}'`
                    echo -e "${c_big}\n$level Origin Log:${c_by} searching $label_en ...... $origin_ip${c_e}"
                    out=${line%\"*}
                    log_format "$out"
                done
                origin_flg=0
                # 如果origin日志找到了，则从origin日志中获取upstream和port
                log_version --uapflv origin.log

            # 没有找到origin日志，则考虑是不是合并回源导致request id变了，针对合并回源的情况，需要在合并回源的机器上，查找uni_rtmp_monitor.log日志，从中拿到relay session id，在过滤error.log匹配relay session和字符'request_id'，
            else
                echo -e "\n${c_bib}稍等，正在处理其他信息，可能需要一些时间...${c_e}"
                # 要取按时间排序的最后一条日志，如果有合并回源，则合并到该节点了
                merge_ip=`cat access.log | tail -1 | awk -F'"' '{print $NF}'`
                # echo merge ip = $merge_ip
                # 找到relay session id
                ssh -o ConnectTimeout=$CT $merge_ip "
                cat $livelog/uni_rtmp_monitor.log* | grep $req_id;
                zcat $livelog/$dirpath/uni_rtmp_monitor.log_${rbplus}* | grep $req_id;
                zcat $livelog/$dirpath/uni_rtmp_monitor.log_${rb}* | grep $req_id;
                zcat $livelog/$dirpath/uni_rtmp_monitor.log_${rbminus}* | grep $req_id;" > monitor.log 2>&1
                relay_id=`cat monitor.log | grep $req_id | head -n 1 | awk -F'"' '{print $14}'`
                [[ $relay_id == '' ]] && { echo -e "${c_br}[IDS-125] 无法找到relay session，需确认uni_rtmp_monitor是否开启，或者手动查询...${c_e}"; exit 125; }

                # echo relay id = $relay_id
                # 找合并回源的request id
                ssh -o ConnectTimeout=$CT $merge_ip "
                cat $livelog/error.log* | grep $relay_id;
                zcat $livelog/$dirpath/error.log_${rbplus}* | grep $relay_id;
                zcat $livelog/$dirpath/error.log_${rb}* | grep $relay_id;
                zcat $livelog/$dirpath/error.log_${rbminus}* | grep $relay_id" > error.log 2>&1
                cat error.log | grep 'request_id' | head -n 1 > log && mv log error.log
                [[ `cat error.log | wc -l` -eq 0 ]] && { echo -e "${c_br}[IDS-126] 无法找到合并回源记录，退出...${c_e}"; exit 126; }
                req_id_new=`cat error.log | awk '{for(i=1;i<=NF;i++) print $i}' | grep -E "^request_id" | tr -d '"' | awk -F ':' '{print $2}'`
                # echo request id = $req_id_new
                if [[ $req_id_new == $req_id ]]; then
                    upper_inner=`cat error.log | awk '{for(i=1;i<=NF;i++) print $i}' | grep -E "^peer:" | tr -d '"' | awk -F ':' '{print $2}'`
                    if [[ $upper_inner == 'unix' ]]; then
                        merger_ip=$merge_ip
                        flg=1
                    else
                        ips -m $merge_ip > inner_ip.log
                        merge_ip=`cat inner_ip.log | grep -w "$upper_inner" | awk '{print $1}'`
                    fi
                    # echo upper_inner = $upper_inner
                    # echo merge_ip = $merge_ip
                    ssh -o ConnectTimeout=$CT $merge_ip "
                    cat $livelog/error.log* | grep $req_id;
                    zcat $livelog/$dirpath/error.log_${rbplus}* | grep $req_id;
                    zcat $livelog/$dirpath/error.log_${rb}* | grep $req_id;
                    zcat $livelog/$dirpath/error.log_${rbminus}* | grep $req_id;" > error.new 2>&1
                    cat error.new  | grep -E "found dummy_session:.* is already pulling" | head -n 1 > log && mv log error.new
                    [[ `cat error.new | wc -l` -eq 0 ]] && { echo -e "${c_br}[IDS-127] 无法找到合并回源记录，退出...${c_e}"; exit 127; }
                    relay_id=`cat error.new | awk '{for(i=1;i<=NF;i++) print $i}' | grep -E "^dummy_session" | awk -F ':' '{print $2}' | tr -d '"'`
                    # echo relay id new = $relay_id

                    ssh -o ConnectTimeout=$CT $merge_ip "
                    cat $livelog/error.log* | grep $relay_id;
                    zcat $livelog/$dirpath/error.log_${rbplus}* | grep $relay_id;
                    zcat $livelog/$dirpath/error.log_${rb}* | grep $relay_id;
                    zcat $livelog/$dirpath/error.log_${rbminus}* | grep $relay_id;" > error.relay 2>&1
                    cat error.relay | grep -E "ngx_rtmp_pull_create_task.*request_id" | sort | head -n 1 > log && mv log error.relay
                    [[ `cat error.relay | wc -l` -eq 0 ]] && { echo -e "${c_br}[IDS-128] 无法找到合并回源记录，退出...${c_e}"; exit 128; }
                    req_id=`cat error.relay | awk '{for(i=1;i<=NF;i++) print $i}' | grep -E "^request_id" | tr -d '"' | awk -F ':' '{print $2}'`
                else
                    req_id=$req_id_new
                fi
                # echo request id = $req_id
                # echo merge_ip = $merge_ip
                # 继续在 merge ip 上查找新的 request id 对应的 Origin 日志，其实应该是在rip_list里查询
                live_log_origin "$rip_list" "$bg_pids" "$current" "$year" "$month" "$day" "$hour" "$req_id" $rbplus
                wait
                log_merge $rip_list $req_id

                cat origin.log | grep -q $req_id
                # 判断是否找到新的 request id 相关日志
                if [[ $? -eq 0 ]]; then
                    echo -e "${c_bib}找到了合并回源新的 request id: $req_id - 后续将使用该 ID 继续搜索${c_e}"
                    cat origin.log | grep $req_id | sort -t'"' -n -k2 -k6 > log && mv log origin.log
                    cat origin.log | while read line; do
                        echo -e "${c_big}\n$level Origin Log:${c_by} searching $label_en ...... $merge_ip${c_e}"
                        out=${line}
                        log_format "$out"
                    done
                    origin_flg=0
                    # 如果 Origin 日志找到了，则从日志中获取 upstream ip 和 port
                    log_version --uapflv origin.log
                # 没找到，则输出提示信息
                else
                    echo -e "${c_bp}\n[IDS-129] [ORIGIN-$level]: 回上层IP是$upstream:$port，在uni_origin.log中未查询到$req_id，退出...${c_e}"
                    exit 129
                fi
            fi
        # 如果边缘Access没有找到request id对应的日志信息
        else
            echo -e "\n${c_br}[IDS-130] [ACCESS-$level]: 无法在$label_en($edge)上找到$req_id，请确认输入的request ID，访问时间或者节点信息是准确的。\n${c_e}"
            exit 130
        fi
    fi
}

#=======================================================================================
# 功能：分层级查询日志链
# 入参：level, live_log_proc
# 出参：None
function live_log_search() {
    # ----------------------------------------------------------------------------
    # 1. 边缘日志搜索处理
    level="Edge" && live_log_proc $level
    # 脚本运行到此处，说明access/origin日志可能都已经找到，upstream和prot也已经获得，需要检查upstream是否是天翼 IP
    ips $upstream > ips.log 2>&1
    # 如果不是天翼IP，则表示该IP可能是源站地址
    [[ $? -ne 0 ]] && { echo -e "${c_bp}\n[IDS-131] 请确认${c_bc} $upstream:$port ${c_bp}是否是源站地址。\n${c_e}"; exit 131; } || cd $trash
    # 如果是天翼IP则表示下一层级还会有相关日志，所以需要找到下一层级的label_en以及对应的rip
    label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    width=`tput cols` && echo
    for i in `seq $width`; do
        echo -ne "${c_bb}=${c_e}"
    done
    echo -e "\n${c_bb}Next Hop IP: $upstream:$port\n${c_e}"

    # ----------------------------------------------------------------------------
    # 2. 父层日志搜索处理
    level="Center" && live_log_proc $level
    # 脚本运行到此处，说明access/origin日志可能都已经找到，upstream和prot也已经获得，需要检查upstream是否是天翼 IP
    ips $upstream > ips.log 2>&1
    # 如果不是天翼 IP，则表示该 IP 可能是源站地址
    [[ $? -ne 0 ]] && { echo -e "${c_bp}\n[IDS-132] 请确认${c_bc} $upstream:$port ${c_bp}是否是源站地址。\n${c_e}"; exit 132; } || cd $trash
    # 如果是天翼IP则表示下一层级还会有相关日志，所以需要找到下一层级的label_en以及对应的rip
    label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    width=`tput cols` && echo
    for i in `seq $width`; do
        echo -ne "${c_bb}=${c_e}"
    done
    echo -e "\n${c_bb}Next Hop IP: $upstream:$port\n${c_e}"

    # ----------------------------------------------------------------------------
    # 3. 中心日志搜索处理
    level="Nation" && live_log_proc $level
    # 脚本运行到此处，说明access/origin日志可能都已经找到，upstream和prot也已经获得，需要检查upstream是否是天翼 IP
    ips $upstream > ips.log 2>&1
    # 如果不是天翼 IP，则表示该 IP 可能是源站地址
    [[ $? -ne 0 ]] && { echo -e "${c_bp}\n[IDS-133] 请确认${c_bc} $upstream:$port ${c_bp}是否是源站地址。\n${c_e}"; exit 133; } || cd $trash
    # 如果是天翼IP则表示下一层级还会有相关日志，所以需要找到下一层级的label_en以及对应的rip
    label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    width=`tput cols` && echo
    for i in `seq $width`; do
        echo -ne "${c_bb}=${c_e}"
    done
    echo -e "\n${c_bb}Next Hop IP: $upstream:$port\n${c_e}"

    # ----------------------------------------------------------------------------
    # 4. 容错日志搜索处理
    level="Unknown" && live_log_proc $level
    # 脚本运行到此处，说明access/origin日志可能都已经找到，upstream和prot也已经获得，需要检查upstream是否是天翼 IP
    ips $upstream > ips.log 2>&1
    # 如果不是天翼 IP，则表示该 IP 可能是源站地址
    [[ $? -ne 0 ]] && { echo -e "${c_bp}\n[IDS-134] 请确认${c_bc} $upstream:$port ${c_bp}是否是源站地址。\n${c_e}"; exit 134; } || cd $trash
    # 如果是天翼IP则表示下一层级还会有相关日志，所以需要找到下一层级的label_en以及对应的rip
    label_en=`cat ips.log | grep "所属资源池" | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    width=`tput cols` && echo
    for i in `seq $width`; do
        echo -ne "${c_bb}=${c_e}"
    done
    echo -e "\n${c_bb}Next Hop IP: $upstream:$port\n${c_e}"
}

#=======================================================================================
# 功能：格式化输出日志，使其字段更容易看懂，输入第一个参数是日志全文
# 入参：access.log, origin.log, v03, ov06, OUT_ACC1, OUT_ACC2, OUT_ACC3, OUT_ORI1, OUT_ORI2
# 出参：格式化输出
function log_format() {
    log=$1
    log_v=`echo $log | awk -F '"' '{print $1}'`
    log_fs='v03 ov06 OUT_ACC1 OUT_ACC2 OUT_ACC3 IN_ACC1 IN_ACC2 IN_ACC3 OUT_ORI1 OUT_ORI2 OUT_ORI3 IN_ORI1 IN_ORI2 IN_ORI3'
    for log_f in $log_fs; do
        if [[ $log_v == $log_f ]]; then
            eval log_f=\$$log_f
            echo $log_f | awk -F '"' '{for(i=1;i<=NF;i++) printf "%s %s\"", "\033[1;3;36m"i"\033[0m", $i} END{print ""}'
        fi
    done
    width=`tput cols`
    echo -n "    "
    for i in `seq $((width-8))`; do
        echo -ne "${c_bip}^${c_e}"
    done
    echo -n "    "
    echo $log | awk -F '"' '{for(i=1;i<=NF;i++) printf "%s %s\"", "\033[1;3;36m"i"\033[0m", $i} END{print ""}'
}

function logfile() {
    if [[ -d $trash ]]; then
        echo -e "${c_br}[IDS-135] 对于同一个用户，同一时间只能运行一个实例，请重新运行...${c_e}"
        exit 135
    else
        mkdir -p $trash
        cd $trash && cd ..
        docs=`ls`
        for doc in $docs; do
            [[ -f $doc ]] && rm -rf $doc
        done
        folders=`ls -t`
        while [[ `echo $folders | awk '{print NF}'` -gt 29 ]]; do
            folder=`ls -t | tail -1`
            rm -rf $folder
            folders=`ls -t`
        done
        cd $trash && touch ids
    fi
}

# ----------------------------------------------------------------------------
# 自定义颜色显示
c_br='\e[1;31m'     # bold red
c_bg='\e[1;32m'     # bold green
c_by='\e[1;33m'     # bold yellow
c_bb='\e[1;34m'     # bold blue
c_bp='\e[1;35m'     # bold purple
c_bc='\e[1;36m'     # bold cyan
c_bir='\e[1;3;31m'  # * bold italic red
c_big='\e[1;3;32m'  # bold italic cyan
c_bib='\e[1;3;34m'  # * bold italic cyan
c_bip='\e[1;3;35m'  # bold italic cyan
c_bic='\e[1;3;36m'  # bold italic cyan
c_e='\e[0m'         # reset
# some initializing action
TS=`date +%s%N`     # *
ts=`date +%s`       # *
stty erase '^H'  # * 允许回退键删除内容
req_id=''        # * 接收入参
year=0           # * year
month=0          # * month
day=0            # * day
hour=0           # * hour
label_en=''      # * 节点英文标签
number=0         # * label_en 中的行数
label=''         # single label name in English
prod_type=1      # * 产品类型
flg=0            # signify if get CTC group successfully, 0 - NG and 1 - OK
access_flg=0     # * 是否在access日志中找到reqid，0表示未找到，1表示找到了
origin_flg=0     # * 是否在origin日志中找到reqid，0表示未找到，1表示找到了
time_range=''    # * input: the specified time to search logs
range_d=0        # * days between now and time_range
range_s=0        # * seconds between now and time_range
current='yes'    # * 判断输入的时间是否是当前时间，默认是yes
CT=60            # * ssh connection timeout
bg_pids=''       # * pid lists which run in background
cdn_access_log='/home/log/cluster_gateway_log'       # *
cdn_origin_log='/home/log/trafficserver'             # *
toolbox='/usr/local/script/fanmf11'                  # *
data='/usr/local/script/fanmf11/data'                # *
host=`whoami`                                        # * 判断执行用户
trash="/usr/local/script/fanmf11/trash/$host/$TS"    # * 每个用户的临时文件存放处
access_ip='0.0.0.0'
origin_ip='0.0.0.0'
upstream='0.0.0.0'
port='0'
upstream_and_port='0.0.0.0:0'

livelog='/home/log/cluster_live_log'
liveatslog='/home/log/trafficserver'

v03='$version"$timeLocal"$request_id"$httpCode"$timestamp"$respondTime"$rwt_time"$wwt_time"$firstDur"$finalize_error_code"$serverIp"$destPort"$clientIp"$clientPort"$method"$protocol"$channel"$url"$httpVersion"$requestBytes"$sent_http_content_length"$bodyBytes"$body_sent"$upstreamAddr"$upstream_status"$mesc"$status"$upstreamIp"$upstream_http_ctl_server_code"$httpRange"$sent_http_content_range"$fileType"$referer"$Ua"$proxyIp"$content_type"$fft_time"$via"$real_client_ip"$attack_type"$dysta"$request_body_length"$ssl_time"$extra1"$extra2"$extra3"$extra4"$extra5"$extra6"$extra7"$extra8"$extra9"$extra10"$extra11"$extra12"$extra13"$extra14"$extra15"$extra16"$extra17"$extra18"$extra19"$extra20"$extra21"$extra22"$extra23"$extra24"$extra25"$extra26"$extra27"$extra28"$extra29"$extra30"$extra31"$extra32"$extra33"$extra34"$extra35"$extra36"$extra37"$extra38"$extra39"$extra40"$extra41"$extra42"$extra43"$extra44"$extra45"$extra46"$extra47"$extra48"$extra49"$extra50"$ex1"$ex2"$ex3"$ex4"$ex5"$ex6"$ex7"$ex8"$ex9"$ex10"$ex11"$ex12"$ex13"$ex14"$ex15"$ex16"$ex17"$ex18"$ex19"$ex20"$ex21"$ex22"$ex23"$ex24"$ex25"$ex26"$ex27"$ex28"$ex29"$ex30"$ex31"$ex32"$ex33"$ex34"$ex35"$ex36"$ex37"$ex38"$ex39"$ex40"$ex41"$ex42"$ex43"$ex44"$ex45"$ex46"$ex47"$ex48"$ex49"$ex50'
ov06='$version"$cqtn"$request_id"$sct"$firstDur"$mesc_milisecond"$clientIp"$nhi"$nhp"$httpCode"$pssc"$cquup"$bodyBytes"$sscl"$rwtms"$sec"$Range_cqh"$Content-Range_psh"$If-Modified-Since_psh"$If-Range_psh"$Via_psh"$dqrtt"$dqrcd"$dqnst"$Host_pqh"$pqsn"$cwr"$pls"$X-Forwarded-For_pqh"$requestBytes"$cqhl"$pqhl"$cqhm"$httpVersion"$Referer_pqh"$User-Agent_pqh"$Cookie_pqh"$serverIp"$pqsp"$protocol"$Content-Type_psh"$Last-Modified_psh"$timestamp"$channel"$server_ssh"$cqtx"$sent_http_content_length"$oqup"$oquq"$accid"$extra2"$extra3"$extra4"$extra5"$extra6"$extra7"$extra8"$extra9"$extra10"$extra11"$extra12'
OUT_ACC1='$version"$protocol"$connect_type"$request_from"$requestTime"$timestamp"$respondTime"$clientIpPort"$connect_time"$publish_play_time"$firstDur"$tcpinfo_rtt"$tcpinfo_rttvar"$proxyIp"$method"$channel"$uri"$appName"$stream"$httpVersion"$url"$httpCode"$recvBytes"$bodyBytes"$upstream_bytes_sent"$upstream_bytes_received"$destIpPort"$hostname"$status"$upstreamAddr"$playDur"$remote_user"$referer"$Ua"$fileType"$httpRange"$http_cookie"$connTag"$firstTag"$connect_status"$ex1"$ex2"$ex3"$ex4"$ex5"$ex6"$ex7"$ex8"$ex9"$ex10"$ex11"$ex12"$ex13"$ex14"$ex15"$ex16"$ex17"$ex18"$ex19"$ex20"$ex21"$ex22"$ex23"$ex24"$ex25'
OUT_ACC2='$version"$protocol"$connect_type"$request_from"$requestTime"$timestamp"$respondTime"$clientIp"$clientPort"$connect_time"$publish_play_time"$firstDur"$tcpinfo_rtt"$tcpinfo_rttvar"$proxyIp"$method"$channel"$uri"$appName"$stream"$httpVersion"$url"$httpCode"$recvBytes"$bodyBytes"$upstream_bytes_sent"$upstream_bytes_received"$serverIp"$destPort"$hostname"$status"$upstreamAddr"$playDur"$remote_user"$referer"$Ua"$fileType"$httpRange"$http_cookie"$request_id"$firstTag"$connect_status"$time_local"$server_rip"$rwt_time"$wwt_time"$relay_url"$session_id"$finalize_error_code"$body_bytes_sent"$response_header_len"$request_body_len"$request_header_len"$sent_http_content_length"$upstream_response_time"$sdtfrom"$ext1"$ext2"$ext3"$ext4"$ext5"$ext6"$ext7"$ext8"$ext9"$ext10"$ext11"$ext12"$ext13"$ext14"$ext15"$ext16"$ext17"$ext18"$ext19"$ext20"$ext21"$ext22"$ext23"$ext24"$ext25"$ex1"$ex2"$ex3"$ex4"$ex5"$ex6"$ex7"$ex8"$ex9"$ex10"$ex11"$ex12"$ex13"$ex14"$ex15"$ex16"$ex17"$ex18"$ex19"$ex20"$ex21"$ex22"$ex23"$ex24"$ex25'
OUT_ACC3='$version"$timestamp"$protocol"$connect_type"$request_from"$hostname"$serverIp"$destPort"$requestTime"$clientIp"$clientPort"$method"$url"$uri"$channel"$appName"$stream"$httpVersion"$httpCode"$respondTime"$recvBytes"$bodyBytes"$upstream_bytes_sent"$upstream_bytes_received"$response_header_len"$body_bytes_sent"$request_header_len"$request_body_len"$status"$firstDur"$tcpinfo_rtt"$tcpinfo_rttvar"$proxyIp"$referer"$Ua"$fileType"$httpRange"$http_cookie"$firstTag"$finalize_error_code"$request_id"$ext1"$ext2"$ext3"$ext4"$ext5"$ext6"$ext7"$ext8"$ext9"$ext10"$ext11"$ext12"$ext13"$ext14"$ext15"$ext16"$ext17"$ext18"$ext19"$ext20"$ext21"$ext22"$ext23"$ext24"$ext25"$ex1"$ex2"$ex3"$ex4"$ex5"$ex6"$ex7"$ex8"$ex9"$ex10"$ex11"$ex12"$ex13"$ex14"$ex15"$ex16"$ex17"$ex18"$ex19"$ex20"$ex21"$ex22"$ex23"$ex24"$ex25'
IN_ACC1=$OUT_ACC1 && IN_ORI1=$OUT_ACC1 && OUT_ORI1=$OUT_ACC1
IN_ACC2=$OUT_ACC2 && IN_ORI2=$OUT_ACC2 && OUT_ORI2=$OUT_ACC2
IN_ACC3=$OUT_ACC3 && IN_ORI3=$OUT_ACC3 && OUT_ORI3=$OUT_ACC3


# ----------------------------------------------------------------------------
# 正常退出时触发
trap 'onExit' EXIT

# 捕获Ctrl+C时触发
trap 'onCtrlC' INT


# ----------------------------------------------------------------------------
# 只接收一个入参
if [[ $# -ne 1 ]]; then
    echo -e "${c_bc}[IDS-136] 该脚本工具只接收一个参数——request ID，作为入参，示例如下：         ${c_e}"
    echo -e "${c_big}1. ids 30b4876065d0be98199db0525530af39                              ${c_e}"
    echo -e "${c_big}2. ids 63f72079_zj-jiaxing2-13_da2daa84cd2498ae - less than 64bits \n${c_e}"
    echo -e "${c_br}注意：对于靠近整点(例如：22:59:01)的日志，查询可能遭遇失败，对应情形可更换日志重新查询\n${c_e}"
    exit 136
fi
req_id=$1 && logfile

echo $req_id | grep -Eq '_'
# 新款request id
if [[ $? -eq 0 ]]; then
    # 获取$label_en, $time_range
    node_time_new
    # 功能：检查输入的时间范围是否符合格式要求：14天内，不能是未来时间，10位数字
    # 入参：time_range
    # 出参：current, year, month, day, hour
    time_check $time_range
    # 功能：获取 prod_type - 产品类型
    # 入参：None
    # 出参：TS, prod_type
    prod_type_inp
    if [[ $prod_type == '1' ]]; then
        # 利用$label_en查找日志链
        cdn_log_search
    elif [[ $prod_type == '2' ]]; then
        echo -e "${c_br}[IDS-137] 还未支持，退出...${c_e}"
        exit 137
    elif [[ $prod_type == '3' ]]; then
        echo -e "${c_br}[IDS-138] 还未支持，退出...${c_e}"
        exit 138
    elif [[ $prod_type == '4' ]]; then
        echo -e "${c_br}[IDS-139] 还未支持，退出...${c_e}"
        exit 139
    elif [[ $prod_type == '5.1' ]]; then
        echo -e "${c_br}[IDS-140] 还未支持，退出...${c_e}"
        exit 140
    elif [[ $prod_type == '5.2' ]]; then
        echo -e "${c_br}[IDS-141] 还未支持，退出...${c_e}"
        exit 141
    elif [[ $prod_type == '5.3' ]]; then
        echo -e "${c_br}[IDS-142] 还未支持，退出...${c_e}"
        exit 142
    elif [[ $prod_type == '5.4' ]]; then
        echo -e "${c_br}[IDS-143] 还未支持，退出...${c_e}"
        exit 143
    elif [[ $prod_type == '5.5' ]]; then
        echo -e "${c_br}[IDS-144] 还未支持，退出...${c_e}"
        exit 144
    elif [[ $prod_type == '5.6' ]]; then
        echo -e "${c_br}[IDS-145] 还未支持，退出...${c_e}"
        exit 145
    elif [[ $prod_type == '5.7' ]]; then
        echo -e "${c_br}[IDS-146] 还未支持，退出...${c_e}"
        exit 146
    else
        echo -e "${c_br}[IDS-147] 请按照如上提示，输入正确的产品类型序号，退出...${c_e}"
        exit 147
    fi
# 老款request id
else
    # 功能：适用于旧版本的reqID，获取$label_en和$time_range
    # 入参：None
    # 出参：time_range, label_en, edge
    node_time_old
    # 功能：检查输入的时间范围是否符合格式要求：14天内，不能是未来时间，10位数字
    # 入参：time_range
    # 出参：current, year, month, day, hour
    time_check $time_range
    # 功能：获取 prod_type - 产品类型
    # 入参：None
    # 出参：TS, prod_type
    prod_type_inp
    if [[ $prod_type == '1' ]]; then
        # 功能：分层级查询日志链
        # 入参：level, cdn_log_proc
        # 出参：None
        cdn_log_search
    elif [[ $prod_type == '2' ]]; then
        live_log_search
    elif [[ $prod_type == '3' ]]; then
        echo -e "${c_br}[IDS-148] 还未支持，退出...${c_e}"
        exit 148
    elif [[ $prod_type == '5.1' ]]; then
        cdn_log_search
    elif [[ $prod_type == '5.2' ]]; then
        cdn_log_search
    elif [[ $prod_type == '5.3' ]]; then
        cdn_log_search
    elif [[ $prod_type == '5.4' ]]; then
        cdn_log_search
    elif [[ $prod_type == '5.5' ]]; then
        cdn_log_search
    elif [[ $prod_type == '5.6' ]]; then
        cdn_log_search
    elif [[ $prod_type == '5.7' ]]; then
        cdn_log_search
    else
        echo -e "${c_br}[IDS-149] 请按照如上提示，输入正确的产品类型序号，退出...${c_e}"
        exit 149
    fi
fi


