【awk】使用awk查询指定traceId的日志(可以查看到完整的Exception堆栈信息)

使用awk查询指定traceId的日志

  • 为什么不直接grep?
  • 方案1(改动logback)
  • 方案2(不改动logback)

为什么不直接grep?

因为日志里面会存在换行的情况,比如Exception的打印,如果我们直接grep,会丢失掉很多Exception的堆栈信息。

方案1(改动logback)

  1. 记录日志时:在应用日志中,将异常打印到一行(通过logback的配置,将换行符替换为特殊的记号,比如这里我们将换行符替换为[Enter])
  2. 查看日志时:在shell中grep出所有包含traceId的行。然后将[Enter]替换为换行符,最后输出即可。
  • 先要配置logback
    主要是配置:%replace(%ex){'[\r\n]+', '[Enter]'}%nopex,意思是讲Exception中的所有换行替换为[Enter],后面我们查看时候再将[Enter]替换成换行即可。
<property name="logPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS}|%X{trace-id}|[%t] %-5level %logger{50} %line - %m %replace(%ex){'[\r\n]+', '[Enter]'}%nopex%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${logPattern}pattern>
    encoder>
appender>
  • 查询日志脚本(命名为search.sh)
#!/bin/bash

trace_id=$1
file_name=$2

if [ -z "$trace_id" ]; then
    echo 'trace_id 参数不能为空'
    exit 2
fi

if [ -z "$file_name" ]; then
	# todo 当不指定名称时的默认值,替换成自己的
    file_name='default_log_name.log'
fi

echo "======================开始在$file_name中查询traceId=[$trace_id]的日志======================"
# todo NF >=3 以及 指定'|'作为分隔符的过滤策略,根据自己的日志格式进行修改
awk -v traceId="$trace_id" -F '|' '{if(NF >= 3 && $2==traceId) print $0; }' $file_name > temp_log_search.log
# 将[Enter]替换成回车
sed -i "s/\[Enter\]/\\n/g" temp_log_search.log
# 查看前一千行(防止太大)
head -1000 temp_log_search.log
echo "======================结束在$file_name中查询traceId=[$trace_id]的日志======================"
exit 0
  • 使用脚本查看日志
./search.sh 851fc4fa2576414c8fdd77ae9c91216f app-2023-03-30-09-0.log

方案2(不改动logback)

此方案主要针对那些不好改动logback的场景(比如领导不让改,或者处于其他一些原因的考虑)

  1. 通过awk给日志文件的每一行行头添加上行号。
  2. 通过grep将包含traceId的行取出,然后暂存到temp_log_search.log中
  3. 通过head和tail方法获取temp_log_search.log的第一行和最后一行,然后通过awk解析出【开始】和【结束】行号
  4. 最后通过awk和解析到出【开始行号】<= 行号 <= 【结束行号】所有的日志。

(当然,因为是直接取了开始行号和结束行号中间的所有行,所以会包含一些其他非此请求的日志混在其中,我们可以通过一些规则,借助awk二次过滤,这里的脚本尽可能得过滤了非此traceId的日志,但有些情况下不彻底,比如在这些行内有其他请求的异常出现,这个大家取舍吧),附上脚本。(方案一不存在这个情况!!!,建议采用方案一)

  • 查询日志脚本(命名为search.sh)
#!/bin/bash
trace_id=$1
file_name=$2

if [ -z "$trace_id" ]; then
    echo 'trace_id 参数不能为空'
    exit 2
fi

if [ -z "$file_name" ]; then
	# todo 当不指定名称时的默认值,替换成自己的
    file_name='default_log_name.log'
fi

echo "======================开始在$file_name中查询traceId=[$trace_id]的日志======================"
# 给每一行添加行号,然后通过trace_id过滤出所有数据行存储到temp_log_search.log中
awk '{print NR ")" $0 }' $file_name | grep $trace_id > temp_log_search.log
# 找到开始行
head_num=`head -1 temp_log_search.log | awk -F ')' '{print $1}'`
if [ -z "$head_num" ]; then
    echo '未查询到内容!'
    exit 2
fi
# 找到结束行
tail_num=`tail -1 temp_log_search.log | awk -F ')' '{print $1}'`

# 打印出开始和结束行的日志(这里尽最大可能过滤掉非此traceId的日志)
# todo NF >=3 以及 指定'|'作为分隔符的过滤策略,根据自己的日志格式进行修改
awk "NR > $head_num && NR < $tail_num" $file_name | awk -v traceId="$trace_id" -F '|' '{if(NF >= 3 && $2==traceId || NF < 3) print $0; }'
echo "======================结束在$file_name中查询traceId=[$trace_id]的日志======================"
exit 0
  • 使用脚本查看日志
./search.sh 851fc4fa2576414c8fdd77ae9c91216f app-2023-03-30-09-0.log

你可能感兴趣的:(linux,运维,服务器,awk,traceId)