#!/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