通过Shell脚本自动化提取日志并上传文件到SFTP服务器

服务器环境:CentOS 6.9

前言

先前做了个系统,按照接口调用次数进行计费。每次的接口调用,会在日志中记录该次接口调用的详细信息(一行日志),包括:

  • 商户:发起该次调用的是哪个商户
  • 接口:该次调用的是哪个接口
  • 个人信息:该次调用传递的个人信息
  • 结果:该次调用的结果,是调用成功还是失败(成功才会计费)
  • 时间:调用发生的时间
  • 唯一id:标识该次调用的唯一id
  • 是否计费:YES/NO

某个商户对某个接口的调用次数,以权益的形式进行预充值,随后调用则在总次数中进行扣减。

后来,由于每个月需要和上下游商户进行对账,需要根据日志进行调用量统计,生成对账报表。比如:某个商户调用某个接口,共多少次,其中有多少次是产生计费的,这样。

本来,公司内部已经有了一个ELK系统(由另外一个部门,姑且称为B,负责),按理说这种日志数据统计的工作,不应该由负责业务系统开发的我,去参与。

我本以为,只需要,给出业务系统部署的机器ip,日志路径,文件命名格式,需要统计的日志格式规范,然后剩下的就是ELK团队的事情了。(在我业务系统的机器上部署一个filebeat,采集日志然后输出到他们的ELK,报表直接通过ElasticSearch和Kibana进行导出即可,我以为是这样)

结果,这次的对账报表,对接的是部门B的结算组,这个组好像完全不知道他们部门还有个ELK(ex me?你们不是一个部门的吗???)

然后,结算组要求我这边,每个月将日志文件,通过SFTP的形式上传到他们指定的机器上(wTF???)。

要知道,我的业务系统,一天的日志量还蛮大的,最近观察到,最大的10/15号,一天就有2G的业务日志,而其中跟计费相关的日志,又是很少的一部分。

唉,其实要做统计的话,我这边直接写几个正则,然后通过grep加个wc -l就可以了,还非得把日志文件推送给部门B的结算组,让他们再做处理。搞不懂。(据产品说,流程就是这样,结算必须通过他们)。

怎么办呢?硬着头皮做呗。

实现

首先,不可能把整个日志文件传过去,太大了,而且里面包含了很多对于统计来说,无用的信息。(统计并生成账单,只需要记录接口调用情况的那一行日志就可以了)。

所以我需要对日志文件,进行信息提取,把提取到的计费日志单独保存在一个文件。随后,再通过sftp上传文件到指定机器。

这种和业务逻辑无关的功能,我的想法是,通过自动化脚本来实现。

具体来说就是,在业务系统所在的Linux服务器上,编写一个Shell脚本,然后配置crontab定时任务,自动化进行日志的处理和sftp上传文件。

首先,业务系统每天会产生1个或多个日志,日志文件名称形如

console-2021-10-15.1.log

我的想法是,编写一个脚本(暂且命名为dailyProcess.sh),该脚本每天凌晨0点之后执行,它要做的事是:对前一天的日志进行处理,提取其中和计费相关的日志,并保存为一个新的文件。

首先,需要获取前一天的日期,使用date命令。

date命令

date -d '-1 day' 获取当前日期前一天的日期

date -d '-1 month' 获取1个月前的日期

date -d '-2 hour' 获取2小时前的时间

date -d '+2 day' 获取2天后的日期

date -d '-1 month -18 day' 获取一个月零18天之前的日期

当然,我们还需要,对得到的日期字符串,进行格式化

date -d '-1 day' +%Y-%m-%d

假设今天是2021-10-21,上面的命令得到的结果就是

2021-10-20

格式化符号之间有空格的话,记得用引号将格式化符号部分括起来,如

date +'%Y-%m-%d %H:%M:%S'

下面是date命令,较完整的格式化符号:

符号含义备注
Y年(4位)例:2021
y年(2位)例:21
m月(2位)例:10
d
H时(0-23)
I时(0-12)
p上午/下午例:PM
M分(0-59)
S
a星期(简写)例:Thu
A星期(全称)例:Thursday
u星期(1-7,1是星期一)例:4
b月份(简写)例:Oct
B月份(全称)例:October
F等价于%Y-%M-%d例:2021-10-21
r等价于%I:%M:%S %p例:02:03:23 PM
R等价于%H:%M例:14:03
T等价于%H:%M:%S例:14:03:23
s自1970-01-01的秒数(就是秒级的时间戳)例:1634796275
z时区例:+0800
Z时区(英文)例:CST

完整的表格可以通过帮助进行查看:date --help

继续说回这次的需求。通过date命令获取到前一天的日期,并且保证格式是我所需要的。随后,进入到服务器存放日志的目录下,列出前一天日期的日志,比如:ls | grep 2021-10-20

grep命令

对这些列出的文件进行遍历,并用grep命令抓取其中的计费部分日志,比如计费日志固定是一行,且以logType开头,则shell脚本形如

lastDay=2021-10-20
newFile=/log/newLogs/$lastDay.log
for file in `ls | grep $lastDay`
do
grep -o 'logType.*' $file >> $newFile
done

通过正则表达式,会将前一天的日志中的计费日志那一行,抓取出来,并输出到一个新的文件中。这里注意,若前一天有多个文件,则最终只会生成一个文件,需要注意用>>,这样会以追加的形式(append)写入到文件。另外注意,使用grep命令时,添加-o参数,则只会抓取匹配部分(–only-matching),可以减少无用信息,使得最终生成的日志文件尽可能的小。

假设原始日志中有如下的计费日志信息(占一行)

2021-10-21 00:00:01.371 INFO http-nio-8027-exec-340 com.yogurt.dbj.aop.LogAop [1634745600986] - logType:verify,enterpriseName:某宝,enterpriseType:real,secretId:abcd,enterId:123,requestId:1634745600986,apiName:大宝剑服务,supplierName:某东,userName:麻*疼,userIdNo:123***456,userPhoneNo:135****1111,verifyResult:一致,cost:1,time:2021-10-21 00:00:01.371

如果grep不加-o参数,则会将这一行整行抓取并写入到新文件;加了-o,则只会抓取

logType:verify,enterpriseName:某宝,enterpriseType:real,secretId:abcd,enterId:123,requestId:1634745600986,apiName:大宝剑服务,supplierName:某东,userName:麻*疼,userIdNo:123***456,userPhoneNo:135****1111,verifyResult:一致,cost:1,time:2021-10-21 00:00:01.371

能减少一部分无用信息。

随后,由于日志中包含了姓名,手机号,身份证等敏感信息,而结算时不允许接收这种敏感信息,故需要进行再处理,将这些敏感信息剔除,这里采用的是sed命令

sed命令

sed命令可以用来对文件进行编辑,内容替换等操作。我们想把日志中的敏感信息删掉,则用下面的命令

sed -i "s/userName.*userPhone.*,verifyResult/verifyResult/g" $newFile

其中-i--in-place)表示,直接读取并修改文件内容,而不输出到终端,后面的字符串是需要执行的操作,开头的s表示执行替换,其格式为:s/原字符串/新字符串

结尾的/g表示全局进行替换。所以上面的命令就表示

将新的文件中的能够匹配这个正则userName.*userPhone.*,verifyResult的内容,替换为verifyResult,其实就是删除了userName到verifyResult之间的内容。(由于sed命令的正则表达式,不支持懒惰模式,所以采取了这种方式,若支持懒惰模式的正则,则可以这样写:s/userName.*userPhone.*?,//g),表示匹配以userName开头,匹配到userPhone后面的第一个,结束,并用空字符串替换(.*表示匹配任意字符(非换行符)任意次,并默认采用贪婪模式,就是尽可能匹配多次,而.*?采用懒惰模式,尽可能匹配少次)。

这样,每天的日志通过这个shell脚本,能够提取出其中的计费日志部分。随后,每一个月1号,再通过一个shell脚本,将之前提取出的,上个月的全部日志文件,上传到指定的sftp服务器即可。

sftp命令

由于要写shell脚本,而sftp命令无法直接将账密作为参数带在命令后面,所以我们需要借助lftp命令,若服务器上没有lftp命令,直执行yum install lftp即可

随后可以借助lftp命令自动连接sftp服务器并上传文件,如下

sftpIp=0.0.0.0
sftpUser=xxx
sftpPassword=xxx

lftp -u ${sftpUser},${sftpPassword} sftp://${sftpIp}

这样就实现了自动连接sftp服务器

那么连接服务器后,如何发出命令呢,我们将命令通过一个字符串传递过去

sftpIp=0.0.0.0
sftpUser=xxx
sftpPassword=xxx

lftp -u ${sftpUser},${sftpPassword} sftp://${sftpIp} <<EOF
cd xxx
put console-2021-10-15.log
by
EOF

其中的

<<EOF
abc
EOF

abc表示用户输入的内容,而EOF是标识符,标识用户输入的开始和结束,EOF也可以被替换成任意其他字符,比如

<<ABC
abc
ABC

所以,上面的命令就是,连接上sftp服务器后,模拟用户输入cdput等命令,完成文件上传的操作。

如此以来,我们编写了2个脚本:dailyProcess.shmonthlyProcess.sh

前者每天凌晨执行,对前一天的原始日志进行处理,提取其中的计费日志部分,生成一个新的文件(称为计费日志吧);

后者每月1号凌晨执行,将上个月所有的计费日志,上传到sftp服务器

配置crontab

现在,需要对这两个脚本文件,编写定时任务。

我们需要crontab这个命令

crontab -l 可以列出当前用户的定时任务

crontab -e可以编辑当前用户的定时任务

若想要查看所有用户的定时任务,则进入到目录/var/spool/cron查看即可,若用户root拥有几个定时任务的配置,则在该目录下存在一个文件,名为root,这个文件就是用来配置该用户的定时任务的,若当前以root用户登录,则执行crontab -e打开的也是这个文件。

另外,还可以在/etc/crontab这个文件来编辑定时任务,/etc目录下和定时任务有关的配置文件有:

cron.d
cron.daily
cron.deny
cron.hourly
cron.monthly
crontab
cron.weekly

通常来说,查看/etc/crontab这个文件,就可以知道Linux下的定时任务的cron表达式要如何写了,如下

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

放在这里的定时任务配置,需要指定执行的用户。而通过crontab -e编辑的是当前用户的定时任务,则不用指定用户。

假设我们上面的dailyProcess.sh需要每天凌晨1点执行,则可以执行crontab -e,并进行如下配置

0 1 * * * /xxx/xxx/dailyProcess.sh >> /xxx/xxx/daily.log 2>&1

注意,配置定时任务时,最好将输出重定向到一个文件,以便观察定时任务的执行情况

类似的,对于另一个脚本monthlyProcess.sh,我们配置为每月1号的凌晨1点执行

0 1 1 * * /xxx/xxx/monthlyProcess.sh >> /xxx/xxx/monthly.log 2>&1

附录

两个脚本的参考文件如下:

dailyProcess.sh

#! /bin/bash

# 配置成定时任务,每天凌晨0点之后运行,提取前一天日志文件中的计费日志,输出到指定目录

last_day=`date -d '-1 day' +%Y-%m-%d`

last_day_month=`date -d '-1 day' +%Y-%m`

logPath=/opt/xxx/logs

newLogPath=${logPath}/newLogs/${last_day_month}

#创建保存新日志的目录
if [ ! -e "${newLogPath}" ]
then
        mkdir -p ${newLogPath}
fi

echo 开始进行计费日志提取,处理该日期的日志:  ${last_day}

# 开始处理前一天的日志

cd ${logPath}

# 新生成的文件
newFile=${newLogPath}/console-${last_day}.log

# 处理前一天的日志(可能有多个),提取出计费日志,写入到新的日志文件
for file in `ls | grep ${last_day}`
do
        grep -o 'logType:operatorVerify.*' $file >> ${newFile}
done

# 对提取出的日志进行再处理,抹掉姓名,身份证,电话等敏感信息
sed -i "s/userName.*userPhone.*,verifyResult/verifyResult/g" ${newFile}

# 处理完毕
echo 处理完毕

monthlyProcess.sh

#! /bin/bash

# 处理上个月的数据
last_month=`date -d '-1 month' +%Y-%m`

# 这里是生产环境的日志目录, 测试环境需要修改
logPath=/opt/xxx/logs

# 需要处理的日志存放的目录
newLogPath=${logPath}/newLogs/${last_month}

cd ${newLogPath}

sftp_ip=0.0.0.0
sftp_user=xxx
sftp_password=xxx

# 挨个上传文件
for i in `ls | grep console`
do
echo uploading $i
lftp -u ${sftp_user},${sftp_password} sftp://${sftp_ip} <<EOF
cd xxx
put $i
by
EOF
done

echo 文件上传完毕

# 执行统计

output=${newLogPath}/${last_month}-result

echo "准备执行数据统计..."


api[0]="api0"
api[1]="api1"
api[2]="api2"
api[3]="api3"

for i in $(seq 0 3)
do
        totalPattern="apiName:${api[$i]},supplierName:某宝"
        billedPattern="apiName:${api[$i]},supplierName:某宝,verifyResult.*一致"
        echo "正在统计接口: ${api[$i]} 的调用情况"
        totalNum[$i]=`find ./* | xargs grep "${totalPattern}" | wc -l`
        billedNum[$i]=`find ./* | xargs grep "${billedPattern}" | wc -l`
done

for i in $(seq 0 3)
do
        echo "接口: ${api[$i]} 的总调用量为: ${totalNum[$i]}, 计费调用量为: ${billedNum[$i]}" >> ${output}
done

echo "统计完毕, 统计结果已输出到文件: ${output}"




Shell脚本参考这篇文章:Acwing - Linux基础课(三)- Shell

正则表达式参考这篇文章:一文看懂正则表达式

附录:
使用 lftp 连接上sftp服务器后,可以通过help来查看支持的命令。可以批量删除 mrm,批量上传mput等,批量下载mget等。

(完)

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值