获取发布平台日志并去重通知

定时获取发布日志并去重通知 
     想想都是泪,我竟然加班到现在 = =,明天就是国庆!!!
  下班的时候大家都走的蛮快,我也不例外,然而差不多走到地铁站时,领导来电需要确认两个事,其中一个就是问我发布通知做好了没,我说还有点问题,主要体现在:
(1)部署到定时任务上,定时任务自动把我脚本上用到的判断文件清空(root 跑的,后来我换了别的服务器用普通用户跑,清空现象自动消失)
(2)发过的日志消息重复发:这个确实是我脚本逻辑问题!!加班后已解决。
  于是领导建议我今晚加班做完,这样第二天可以安心放假。
  话说这个需求源于周二的一个会议:业主方说对于xxx网(公司内网有个发布后台,经gitlab同步到线上),你们不能只是想到外部攻击(网站被黑,内容被篡改等),还要完善内部管理,万一真的有内部人员搞搞震,故意或者误操作发了些有问题的文章,造成恶劣影响,你们要有个可追溯历史或日志之类的,定位到是谁做的事。当时,我们市场部经理给人家拍胸口做保证:技术(也就是我)到时会做个发布通知,xx人xx发了什么文章,做了什么事,我们一目了然。
     所以,就有了这个需求:从一个叫 jeecms 的发布操作日志拿到最新信息,发到钉钉群(本来如果专心做这个程序,估计一天搞出来,但是每天总有各种杂事 = =,所以从周三开始调到今天)。当时接到需求,我是打算从git针对文章目录有提交历史就发通知进行思考的。但庆幸的是,竟然发布平台有这个后台操作日志。
一、需求分析
  如下图:
  记录了 ID、用户、时间、IP、标题、内容
  更幸运的是,我找到了数据库对应的日志表,不过跟上面显示的操作日志稍有不同,我贴下表结构(有些没用的字段我就不弄上去):
CREATE TABLE `日志表` (
  `log_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `log_time` datetime NOT NULL COMMENT '日志时间',
  `ip` varchar(50) DEFAULT NULL COMMENT 'IP地址',
  `title` varchar(255) DEFAULT NULL COMMENT '日志标题',
  `content` varchar(255) DEFAULT NULL COMMENT '日志内容',
。。。。 PRIMARY KEY (`log_id`) ) ENGINE
=InnoDB AUTO_INCREMENT=166581 DEFAULT CHARSET=utf8 COMMENT='CMS日志表';
对应表查出来的结果如下:

分析1:所以如果单纯查日志表,显示的字段:user_id是没法看的,需要找另一张用户表来根据 user_id 找到账号名。 ----     sql查询要做表关联

分析2:大家可以留意下发布后台的“时间” ,也就是查数据库日志表时,根据 log_time 逆序排序显示

select * from {日志表} order by log_time desc;

  获取最新后台日志信息也应该逆序去查会比较好,方便跟后台日志对应上,但是发到钉钉可能会有点不习惯,毕竟是逆序显示,所以假设定时任务5分钟扫描一次日志表,获取最新信息,前5分钟内做了多个动作,通知的话为了好区分,我加了个脚本检测时间,和实际用户操作时间(日志表查到的):

分析3:发过的信息不要重复发。

 可以利用日志ID,因为是唯一标识,而且每增加一条日志记录,ID编号会自增1,所以重复信息我们可以根据这个 ID 去判断。当前检测时间如果发现有新增ID(对应变量:ID_New),跟之前的记录文件(对应变量 ID_Old)比较(用命令diff),找出不同 ID 号,去数据库查出日志记录即可(对应变量:Ct_New)。

 --------------------------------------

  最后,讲下注意点

注意点 1:

        每天一个日期目录,因为每天都会有几个记录文件用于当天判断,也方便后续清理删除。我说下每个文件分别代表的意思

(1)logid_old(变量:ID_Old):当天内,前一次检测时间所记录的日志ID,用于判断是否曾经发过钉钉通知的

(2)logid_new_src(变量:ID_SNew):当天内,从当天0点到当前检测时间,查出来的所以日志ID

(3)logid_new(变量:ID_New):去掉(2)文件原始查询结果的关键字 log_id 行

(4)dif:如果有新增日志,logid_new 比 logid_old 多出来的日志ID。假设当时时间new文件的日志ID为:1、2、3,而old文件只有1、2,则dif 文件找到日志ID:3,但是有无用行,如下图第一行

(5)dif_num:找到日志ID在(4)中所在的行号,比如下图的日志ID:166580 是在 dif 文件的第2行,166579 在第 3 行

我这样处理是为了从dif_num 记录的行号找到 dif 里的日志ID,过滤出来就可以找到新增日志ID的具体内容

(6)content_src(变量:Ct_SNew):新增日志 ID 的具体内容,查出来有无用行(日志表的表字段)。需要过滤得到真正日志内容,也就是下面的(7)

(7)content_new(变量:Ct_New):新增日志 ID 的具体内容

 

注意点 2:shell 传参如果有空格会被截断,所以里面有空格过滤

  大家留意下,下面的脚本有个针对变量:content2 进行空格删除,因为日志表字段content,实际上是记录该账号做了什么操作,假设 title 为“增加文章”,content记录文章id,及titie=文章标题

   然而,我发现文章标题起名不规范,里面还有空格,导致钉钉通知被截断,右边书名号都没了,内容发了一半,比较坑爹,如下图:

     为了能发,我把标题空格去掉,牺牲点美观(我发现换表符\t也解决不了,所以暂时先这样处理)

  另外content这个字段有时候查出来为空,当字段title记录“登录成功”的时候

 

   所以下面会有个判断,登录成功,content2=""

  另外一些判断也是因为各种理由,为了通知美观点,可读性强点,就不累赘了。

 

注意点 3:传参到钉钉通知函数

  那个参数传递顺序要注意下,我一开始是把token放到最后的,但会出问题,因为content2有可能为空,导致发信息的字段存在错乱问题,所以后来我改成放第一个参数去传递给钉钉函数。

SendMessageToDingdingljy ${token1} ${user} ${time} ${title} ${content2} 

      另外下面这个写法是我参考一个开发写的python程序,感觉挺聪明的~~~~拿来即用。

    因为有多个钉钉群,有些是我测试看的,有些是发领导看的,屏蔽也方便,适用于需要发多个钉钉群的情况,可以写成这样,不同钉钉群的机器人这样传递就可以了

Dingding_Url=https://oapi.dingtalk.com/robot/send?access_token=$1

二、脚本及效果图
1、脚本代码:

#!/bin/bash

Dir='/home/xxx/xxx-content'
log_path=`date +"%Y%m"`
d=`date +"%d"`
ymd=`date +"%Y-%m-%d"`

Ymd_Dir=${Dir}/${log_path}/${d}

## 每天一个年月日表
[ ! -d ${Ymd_Dir} ] && mkdir -p ${Ymd_Dir}

ID_SNew=${Ymd_Dir}/logid_new_src

ID_New=${Ymd_Dir}/logid_new
ID_Old=${Ymd_Dir}/logid_old

#日志ID原始文件,不存在则创建
[ ! -f ${ID_Old} ] && touch ${ID_Old} 

#日志内容文件
Ct_SNew=${Ymd_Dir}/content_src
Ct_New=${Ymd_Dir}/content_new

cd ${Ymd_Dir}

## 当天0点
begin_time=`date +"%Y-%m-%d 00:00:00"`

## 当前检测时间
end_time=`date +"%Y-%m-%d %H:%M:%S"`

## ljy的测试群
token1='钉钉token'

## 公司群
token2='钉钉token'

# 发钉钉群
function SendMessageToDingdingljy(){
    Dingding_Url=https://oapi.dingtalk.com/robot/send?access_token=$1
    d_t=`date "+%F"_"%T"`
    #d_t=`date +"%T"`
    User=$2
    Time=$3
    Title=$4
    Content=$5

    
    #发送钉钉消息
    curl -H "Content-Type: application/json" -X POST --data '
    {
     "msgtype": "text",
     "text": {
         "content":
             "------ xxx网 ------
             \r检测时间:'${d_t}'
             \r登录用户:'${User}'
             \r用户操作时间:'${Time}'
             \r标题:'${Title}' '${Content}'
     "},
     "at": {
         "atMobiles": [
             "我的手机号码"
         ],
         "isAtAll": false
     }
}' ${Dingding_Url} > /dev/null 2>&1
}


## 1、查 日志id(唯一):今天0点~当前检测而时间
mysql -u{数据库登录用户} -p'{数据库登录密码}' -h{数据库登录地址} -e "use {数据库名}; select {日志ID} from {日志表} where {时间字段} >= '$begin_time' AND {时间字段} <= '$end_time' order by {时间字段} desc;" > ${ID_SNew}


#去掉首行无用行,只要查出来的id
cat ${ID_SNew} |grep -v "log_id" > ${ID_New}


## 2、检查是否有新增:log_id
diff ${ID_New} ${ID_Old}
status=`echo $?` 

>${Ct_SNew}
## 3、如果有新增:log_id,表示后台日志有新内容
if [ ${status} -ne 0 ]
then
   
   ## ID_New 内容肯定要比 ID_Old内容多
   diff ${ID_New} ${ID_Old} > dif
     
   # 找到log_id对应的文件行号,去掉第一行无用行
   sed -n '/</=' dif > dif_num

# 根据行号,找到 log_id
cat dif_num | while read line do #根据行号,找到 log_id 的日志内容 Dif_ID=`sed -n "$line"p dif |awk '{print $2}'` echo "Dif_ID: $Dif_ID" #3.1 根据 log_id,查新增内容 mysql -u{数据库登录用户} -p'{数据库登录密码}' -h{数据库登录地址} -e "use {数据库名}; SELECT users.username, log.log_time, log.ip, log.title, log.content from {日志表} log, {用户表} users where log.user_id=users.user_id and log_id='$Dif_ID' and log_time >= '$begin_time' AND log_time <= '$end_time';" >> ${Ct_SNew} done #3.2 去掉无用行 cat ${Ct_SNew} |grep -v "content" > ${Ct_New} #3.3 查新增内容 cat ${Ct_New} |while read line do user=`echo $line | awk '{print $1}'` time=`echo $line | awk '{print $3}'` title=`echo $line | awk '{print $5}'` if [ "$title" == "修改用户" ]; then content=`echo $line | awk -F'username=' '{print $2}'` else content=`echo $line | awk -F'title=' '{print $2}'` fi #3.3.1 如果日志内容为空,表示登录 # 登录失败会显示密码,也要过滤掉 if [ ! -n "$content" ] || [ "$title" == "登录失败" ]; then content1="" elif [ "$title" == "修改用户" ]; then content1=`echo $content` else content1="《$content》" fi #去除标题内容里面的空格 content2=`echo ${content1// /}` echo "content2= $content2" #3.3.2 钉钉通知 SendMessageToDingdingljy ${token1} ${user} ${time} ${title} ${content2} SendMessageToDingdingljy ${token2} ${user} ${time} ${title} ${content2} done #3.4 替换最新log_id文件,防止重复发钉钉 cp ${ID_New} ${ID_Old} fi

2、运行效果图:

  

 

   最后感叹下,用高级语言写应该没那么累赘,有机会改成python脚本~~~

posted @ 2022-09-30 22:27  windysai  阅读(31)  评论(0编辑  收藏  举报