一个shell解析文件的需求

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 一个shell解析文件的需求

预计阅读时间:7分钟

由于Oracle没有MySQL或EDB慢日志这种机制,因此最近有一个运维的需求,需要解析应用的日志,进行SQL语句执行时间的统计,进而进行监控。

每个应用集群中的节点,会生成自己的处理日志,日志采用循环复写的机制,单个文件512MB,一天最多产生10个,日志文件中待检索的格式,模拟如下,

第一行:时间戳(yyyymmdd-hh24miss.sss-唯一交易ID

第二行:备注信息

第三行:SQL语句原文/SQL参数/开始/结束标识

虽然某一时刻,最多可能有10个日志,但由于选择5-10分钟采集一次,考虑到系统并发的业务需求,因此这段时间内产生多个新日志的可能性并不是很高,所以打算采用抽样的方法,每次到达采集的时间点,只选择解析最新的一个日志,同时,为了降低对应用节点,正常业务的影响,采用分布处理的方法,应用节点上会扫描日志,生成中间日志,管理节点定期采集中间日志,在管理节点上,进行统计和分析,思路如下:

这里先考虑第一步操作,即如何生成中间日志。

下面是一个原始日志的例子:


20180601-120000.100-10000
TEST1
START

...

20180601-120000.150-10000
TEST1
SQL[SELECT * FROM A WHERE ID = ?]

...

20180601-120000.200-10000
TEST1
PARA[1]

...

20180601-120000.300-10000
TEST1
END

已知:

  1. 提供了“SQL语句原文/SQL参数/开始/结束标识”的关键字,例如START、SQL、PARA和END。

  2. 第一行包含本次交易的唯一ID,用于关联本次交易的操作。

  3. SQL执行时间,用END的时间-START的时间,上例中120000.300-120000.100=200毫秒。

  4. 除了上述这四种,由于多线程操作,所以日志中会有其他信息,穿插在这四种中,统计的时候,需要排除除上述信息外,其他的日志信息。

  5. 第一行包含本次交易的唯一ID,用于关联本次交易的操作。

  6. 除了上述这四种,由于多线程操作,所以日志中会有其他信息,穿插在这四种中,统计的时候,需要排除除上述信息外,其他的日志信息。

对于中间日志的生成,打算做到轻量级,因此选择了shell脚本,不依赖于任何第三方工具。

根据上面的需求描述,我们拆开来看,需要做几件事情,

  1. 按照时间排序,得到最新生成的日志名。

  2. 找出原始日志中,所有“SQL语句原文/SQL参数/开始/结束标识“相关的行,包括关键字所在的行,以及前两行,用于统计的时候,可以根据交易ID,将不同的操作串起来。

  3. 生成中间日志。

  4. 找出原始日志中,所有“SQL语句原文/SQL参数/开始/结束标识“相关的行,包括关键字所在的行,以及前两行,用于统计的时候,可以根据交易ID,将不同的操作串起来。

针对需求1,


ls -lt $AUDIT_HOME | grep app | head -n 1 | awk '{print $9}'

从思路上看,

ls可以列出所有的文件,使用lt,可以按照时间进行排序,示例如下,


bisal ls -lt
total 2
-rw-r--r--  1 bisal  staff  781  6 26 10:12 app2010010102.log
-rw-r--r--  1 bisal  staff  412  6 26 10:11 app2010010101.log

为了过滤total这行,又由于日志带有app关键字,可以使用grep过滤。


-rw-r--r--  1 bisal  staff  781  6 26 10:12 app2010010102.log
-rw-r--r--  1 bisal  staff  412  6 26 10:11 app2010010101.log

head -n选择第一个文件。


-rw-r--r--  1 bisal  staff  781  6 26 10:12 app2010010102.log

日志文件名在这行第九位,使用awk打印$9即可。

针对需求2,


grep -B 2 -E "START|SQL|PARA|END" 日志文件名

grep的B参数,可以帮我们找出,匹配关键字所在行之前N行,


-B num, --before-context=num
   Print num lines of leading context before each match.
   See also the -A and -C options.

真的很强大,一个grep -B,就能完成此需求,但其实一开始,走了一些弯路,当时考虑使用awk,记录前一行,再根据下一行,是否匹配关键字,判断打印这两行,可这种做法,无法打印第一行,即包含交易ID的行,


awk '/START/SQL/PARA/END/{print a" "$0}{a=$1}' 日志文件名

由于包含多个关键字,因此grep需要使用-E,


-E, --extended-regexp
    Interpret pattern as an extended regular expression 
    (i.e. force grep to behave as egrep).

针对需求3,

这就很简单了,使用定向到本地文件中,


grep -B 2 -E "START|SQL|PARA|END" 日志文件名  中间日志文件名

这里的中间日志,仅包含这几个匹配的关键字以及前三行的日志内容,作为SQL执行时间解析的输入。

虽然shell很轻量,但要讲究起来,还是有很多技巧,例如,可以将一些文件名和路径,定义为变量,还可以为应用加上日志,以及操作时间戳。曾经借鉴研究了一些,可以参考《》。

以上完整的实现,示例如下:


#!/bin/bash
######################################################
# $Name:       scan_log.sh
# $Version:     v1.0
# $Function:    Scan log to monitor SQL
# $Author:     Bisal
# $Create Date:  2018-06-30
######################################################

HOME=/home/app
FILE_NAME=`ls -lt $HOME | grep app | head -n 1 | awk '{print $9}'`
LOG=$HOME/$FILE_NAME
OUTPUT_FILENAME=/home/app/audit.log
APP_LOG=/home/app/run.log

echo "$(date "+%Y-%m-%d") $(date "+%H-%M-%S"): Scan Start"  ${APP_LOG}
grep -B 2 -E "START/SQL/PARA/END" $LOG  $OUTPUT_FILENAME
echo "$(date "+%Y-%m-%d") $(date "+%H-%M-%S"): Completed"  ${APP_LOG}
echo " "  ${APP_LOG}

在此过程中,感谢饿了么的学明兄、京东方的Hurley以及Nasdaq-NYC的James的指教和建议。

如果您觉得本文有帮助,欢迎关注转发:bisal的个人杂货铺,

一个shell解析文件的需求
本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 一个shell解析文件的需求


 上一篇
SQL语句Concatenation字符串拼接错误 SQL语句Concatenation字符串拼接错误
预计阅读时间:7分钟老旧的Tuxedo服务,测试环境接收一种新报文,处理过程中报错,从应用日志看,提示的是ORA-01489,以前没见过的一种错误。 这是在Solaris平台下,使用Pro*C开发的Tuxedo应用,出错部分的代码模拟如下,
下一篇 
碰见.swp文件咋办? 碰见.swp文件咋办?
预计阅读时间:6分钟 《》编写过程中,曾碰见一个问题,算是个小知识点,模拟操作如下。 一个session用vi打开文件,但不退出,或者直接关闭session,造成异常退出的场景, vi test.txt 另一个session打开相同的文