shell通过log监控流程

任务背景:

一个应用程序运行的时候,可能需要查看多个模块的状态,如gps是否锁定,eeprom是否读写正常等。通常我们是通过过滤log的方式进行查看。看到有的公司提供了界面化的工具,显示当前进程执行了哪些步骤,以及哪些步骤未执行。因此想仿写一个简化版。

实际工作中,遇到了多板卡开发的情况,每个板卡都有一个终端,查看模块状态非常麻烦。以小区建站、时钟同步为例,经常需要在不同的板卡开log,grep 关键字。如果监控的模块多了,简直就是反人类了。。。

预计方案:

1,每个板卡加一个log收集进程,持续查看各板卡的log文件,并将结果发送到网络。在某一板卡或者电脑上接收并展示。(每个板卡需要一个独立发送进程,需要接收端)

2,修改原有的log收集逻辑,在保存本地的同时,发送到网络。在某一板卡或者电脑上接收并展示。(需要改动zlog配置文件,不确定能不能行,需要接收端)

3,在电脑或者某一板卡上,通过sshpass + tail 收集其它板卡的log,然后统一显示。

最终方案:(使用预计方案3)

BBU/zpp 收集所有板卡的信息,并统一显示。(不用主控的原因是,在主控上对BBU用ssppass会失败,同时最终成品,zk没有对外接口)

1,新增shell脚本,用于收集信息,定时刷新,颜色区分显示。

2,新增cfg文件,用于配置有几个板卡,显示多少条SHOW_NAME。每条SHOW_NAME变色的条件是?并打印信息。

3,创建tmp目录,分等目录,然后tail -F收集log并写入。这里面要有断线重连功能。

配置文件说明:

#item like: info##title
#item like: show##dev##SHOW_NAME##LOG_OK##LOG_ERR
#item like: hide##dev##SHOW_NAME##LOG_OK##LOG_ERR
#dev bbu, qck, zkb, zpp
#show use 3 color ,grey(no find log) ,green(find ok log), red(find err log)
#hide use 2 color ,no show(no find log) ,green(find ok log), red(find err log)
#if you do not want use LOG_OK or LOG_ERR, use log_def_ok or log_def_err instead
#support chinese, but should change terminal font to UTF-8.

示例:

info## 
info##Status meaning: 1_unready 2_ready 3_setuping 4_setuped 5_error
show##zkb##APK link status ##apk link status connected##apk link status disconnect
hide##zpp##Optical port err##log_def_ok##oam_zpp_optical_port_reset error

没有收到消息时,展示所有的SHOW_NAME,并且都是灰码展示。

如果收到 MQTT 消息,则文字产生变化。

如果收到 LOG_OK 消息,则SHOW_NAME发生变化。例如变绿

如果收到 LOG_ERR 消息,则SHOW_NAME发生变化。例如变红

每5S刷新一次。

1, 先解析配置文件,确认哪些东西需要展示,并顺序显示初始状态。

1.1 while循环,从配置文件中顺序把需要展示的数据弄出来,在shell中存储。这部分的目的是过滤注释。

1.2 创建string buffer: str_type[] , str_name[] , str_msg[] , show_flag[] , show_buf[]

str_type的作用是存储配置文件中的SHOW_TYPE

str_name的作用是存储配置文件中的SHOW_NAME

str_msg的作用是存储配置文件中的SHOW_MSG

show_flag的作用是判断是否收到相应的log或者MQTT

show_buf的作用是存储将要输出到终端的字符串

2, 收集需要的log消息,准备进行处理。

2.1 开启新线程,收集log消息,并保存在本地。 bbu_log,fh_log,zk_log,zpp_log,

这里有个问题啊,tail -F收集log的时候不能中断。如果log太多,是不是要做ring_buffer?

2.2 过滤log,与str_msg进行比对,根据比对结果更新show_flag。(5S循环一次)

3, 处理完log消息后,更新显示的状态。

3.1 while循环后,每5S刷新一次显示,输出show_buf ,每个小字符串80字符长度。。

由于shell对二维数组的支持较差,所以选择用给一个大数组来弄。然后取出每个block的范围。

由于不能用二维数组,eval在有的板卡上不能使用,导致用起来不是很顺手。

4,get log的问题。

log可能会很大,为了不占用太大的存储资源,因此使用循环buffer的方法。放弃了,循环buf收益比想象中小。主要是发现空间足够,爽。

具体实现:

#!/bin/bash
#### need null str check
sh_home=$(cd "$(dirname "$0")";pwd) 
if [ -z "$1" ] ; then
    echo "exec stdbuf -oL bash ${sh_home}/flow_show.sh start"
    exec stdbuf -oL bash ${sh_home}/flow_show.sh start
fi

if [ "$1"x != "start"x ] ; then
    echo "do not use any parm, please use like bash /bin/flow_show.sh"
    exit
fi

tmp_dir=/home/fshow_tmp
cfg_file=/conf/cam/flow_show.cfg
cur_dev="bbu"
dev_name=("zkb" "bbu" "qck" "zpp")
dev_ip=("10.16.1.1" "10.16.1.10" "10.16.1.12" "10.16.1.13")
dev_usr=("root" "root" "root" "root")
dev_psw=("root" "root" "root" "root")
cfg_len=0
dev_log_path=("/mnt/log/cam.log" "/var/log/cam.log" "/flashDev/log/cam.log" "/flashDev/log/cam.log")
exit_flag=1
echo_time="\033[40;33;4m"
echo_ok="\033[40;32;1m"
echo_err="\033[40;31;5m"
echo_nofind="\033[40;37;2m"
echo_end="\033[0m"
echo "dev_name len ${#dev_name[*]}"
set -u
##${#array}计算数组长度
##func
#msg_type: time, nofind, hide, ok, err, info.
init_func()
{
    rm -fr ${tmp_dir}
    mkdir -p ${tmp_dir}
    pkill -9 sshpass
    pkill -9 tail
    trap "echo ' trapped Ctrl+C' ; pkill -9 sshpass ; pkill -9 tail ; exit 0" SIGINT
    echo "init; trapped Ctrl+C; pkill -9 sshpass; pkill -9 tail"
}

read_cfg()
{
    local i=0
    local tmp_line=""
    local dev=0
    for dev in ${dev_name[@]}
    do
        msg_type[$i]="time"
        str_dev[$i]=${dev}
        str_name[$i]="[${dev}_time]:"
        msg_ok[$i]="init"
        msg_err[$i]="init"
        let i++
    done
    while read readline
    do
        tmp_line=`echo ${readline} | tr -d "\r\n"`
        #echo readline $readline
        #echo tmp_line $tmp_line
        if [ "show"x == "${tmp_line: 0 : 4}"x ] ; then
            msg_type[$i]="nofind"
        elif [ "hide"x == "${tmp_line: 0 : 4}"x ] ; then
            msg_type[$i]="hide"
        elif [ "info"x == "${tmp_line: 0 : 4}"x ] ; then
            msg_type[$i]="info"
            str_name[$i]=${tmp_line: 6}
            str_dev[$i]="init"
            msg_ok[$i]="init"
            msg_err[$i]="init"
            echo "info $i ${str_name[$i]} "
            let i++
            continue
        else
            continue
        fi
        str_dev[$i]="${tmp_line: 6 : 3}"
        tmp_str=${tmp_line: 11}
        str_name[$i]=`echo $tmp_str | awk -F"##" '{print $1}'`
        msg_ok[$i]=`echo $tmp_str | awk -F"##" '{print $2}'`
        msg_err[$i]=`echo $tmp_str | awk -F"##" '{print $3}'`
        #replace '.' '[' '-' '!' for grep
        msg_ok[$i]=`echo ${msg_ok[$i]} | sed 's#\[#\\\[#g;s#\.#\\\.#g;s#\^#\\\^#g;s#\!#\\\!#g;s#\-#\\\-#g'`
        msg_err[$i]=`echo ${msg_err[$i]} | sed 's#\[#\\\[#g;s#\.#\\\.#g;s#\^#\\\^#g;s#\!#\\\!#g;s#\-#\\\-#g'`
        echo "log dev:${str_dev[$i]} name:${str_name[$i]};type:${msg_type[$i]};ok:${msg_ok[$i]};err:${msg_err[$i]}"
        let i++
    done  < ${cfg_file}
    cfg_len=$i
    echo "read finished, cfg len ${cfg_len}"
    return
}

get_log()
{
    echo "${cur_dev} get_log start"
    local loop_max=${#dev_name[@]}
    let loop_max--

    for i in $(seq 0 ${loop_max})
    do
        if [ "${cur_dev}"x == "${dev_name[${i}]}"x ] ; then
            echo "tail -F ${dev_log_path[${i}]} >> ${tmp_dir}/${dev_name[${i}]}.log x"
            tail -F ${dev_log_path[${i}]} | tr -s '\n' >> ${tmp_dir}/${dev_name[${i}]}.log &
        else
            echo "sshpass -p ${dev_psw[${i}]} ssh ${dev_usr[${i}]}@${dev_ip[${i}]} "tail -F ${dev_log_path[${i}]}" | tr -s '\n'"
            sshpass -p ${dev_psw[${i}]} ssh ${dev_usr[${i}]}@${dev_ip[${i}]} "tail -F ${dev_log_path[${i}]}" | tr -s '\n' >> ${tmp_dir}/${dev_name[${i}]}.log &
        fi
        #touch ${tmp_dir}/${dev_name[${i}]}.show
    done
}

update_show()
{
    local dev=0
    local i=0
    local str_tmp=""
    local log_ok_idx=0
    local log_err_idx=0
    local loop_start=${#dev_name[@]}
    local loop_max=${cfg_len}
    let loop_max--
    while [ ${exit_flag} -ne 0 ]
    do
        i=0
        ##prepare show time
        for dev in ${dev_name[@]}
        do
            if [ ! -f ${tmp_dir}/${dev}.log ] ; then
                echo "can not find ${tmp_dir}/${dev}.log"
            else
                str_tmp="`tail -n 1 ${tmp_dir}/${dev}.log | awk '{print $1 " " $2}'`"
            fi
            if [ -z "${str_tmp}" ] ; then
                str_tmp="connecting......"
            fi
            show_buf[$i]="${str_name[$i]} ${str_tmp}"
            let i++
        done
        ##prepare show msg
        for i in $(seq ${loop_start} ${loop_max} )
        do
            if [ "${msg_type[$i]}"x = "info"x ] ; then
                show_buf[$i]="${str_name[$i]}"
                let i++
                continue
            fi
            dev=${str_dev[$i]}
            if [ ! -f ${tmp_dir}/${dev}.log ] ; then
                show_buf[$i]="dev:${dev} not find"
                let i++
                continue
            fi
            show_buf[$i]="[${dev}] ${str_name[$i]} : no find......"

            str_tmp=""
            log_ok_idx=0
            find_msg=${msg_ok[$i]}
            str_tmp=`cat ${tmp_dir}/${dev}.log | grep -n "${find_msg}"|tail -n 1`
            if [ ! -z "${str_tmp}" ] ; then
                msg_type[$i]="ok"
                log_ok_idx=`echo ${str_tmp} | cut -d ':' -f 1`
                show_buf[$i]="[${dev}] ${str_name[$i]} : ${str_tmp}"
            fi

            str_tmp=""
            log_err_idx=0
            find_msg=${msg_err[$i]}
            str_tmp=`cat ${tmp_dir}/${dev}.log | grep -n "${find_msg}"|tail -n 1`
            if [ ! -z "${str_tmp}" ] ; then
                log_err_idx=`echo ${str_tmp} | cut -d ':' -f 1`
                if [ ${log_err_idx} -ge ${log_ok_idx} ] ; then
                    msg_type[$i]="err"
                    show_buf[$i]="[${dev}] ${str_name[$i]} : ${str_tmp}"
                fi
            fi
            #echo -e "${show_buf[$i]}"
            let i++
        done
        clear
        ##show
        loop_max=${cfg_len}
        let loop_max--
        for i in $(seq 0 ${loop_max})
        do
            if [ "${msg_type[$i]}"x == "time"x ] ; then
                echo -e "${echo_time} ${show_buf[i]} ${echo_end}"
            elif [ "${msg_type[$i]}"x == "nofind"x ] ; then
                echo -e "${echo_nofind} ${show_buf[i]} ${echo_end}"
            elif [ "${msg_type[$i]}"x == "hide"x ] ; then
                continue
            elif [ "${msg_type[$i]}"x == "ok"x ] ; then
                echo -e "${echo_ok} ${show_buf[i]} ${echo_end}"
            elif [ "${msg_type[$i]}"x == "err"x ] ; then
                echo -e "${echo_err} ${show_buf[i]} ${echo_end}"
            elif [ "${msg_type[$i]}"x == "info"x ] ; then
                echo "${show_buf[i]}"
            else
              echo "err msg_type[$i] num : ${msg_type[$i]} "              
            fi
        done
        echo ""
        echo ""
        echo ""
        sleep 5
    done
}

################################
##start main
################################
init_func
read_cfg
get_log
update_show
exit
 

LOG##fh_show_name3##module_log_show_default_ok##camnetlink.c:5

dev:zpp
LOG##start##start ok##start err
LOG##zpp_show_name2##camnetlink.c:1##camnetlink.c:3
LOG##zpp_show_name3##camnetlink.c:4##camnetlink.c:5
LOG##zpp show test4##camnetlink.c:9##camnetlink.c:8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值