https://trainoozhou.github.io/2018/09/05/use-linux-shell-monitor-error-log/
前文有講到,最近部署了一個(gè)服務(wù) 但是老是被惡意的掃描 雖然利用nginx禁止了些IP 但我還是想在被惡意掃描時(shí)候收到一個(gè)通知信息 讓我能知道我的服務(wù)器又被訪問了 于是乎,就有了這篇文章
此文目的是為了記錄自己的操作步驟 既給自己一個(gè)復(fù)習(xí)的機(jī)會(huì),同時(shí)也能服務(wù)看到此文的讀者 好了,話不多說 接下來開始正文內(nèi)容
整體的思路如下:
既然是監(jiān)控,比較方便的方式就是利用Linux的cron定時(shí)任務(wù)來定時(shí)去執(zhí)行一個(gè)操作 既然是要能被定時(shí)任務(wù)執(zhí)行的操作,那么我們就需要寫一個(gè)shell腳本 shell腳本需要做什么呢?我們可以去匹配某個(gè)時(shí)間段,在時(shí)間段內(nèi)是否有新的內(nèi)容添加進(jìn)去(日志文件肯定有記錄時(shí)間的),如果有的話,則把這段內(nèi)容單獨(dú)取出來,并發(fā)送郵件通知 上面又涉及到了發(fā)郵件,發(fā)郵件比較好的方式是寫個(gè)Python腳本(因?yàn)槲业姆?wù)器自帶了python環(huán)境,而且python運(yùn)行起來也簡單) 所以,總結(jié)下來我們需要兩個(gè)文件(假定都存放在/opt/mysh/目錄):一個(gè)是可執(zhí)行的shell腳本;一個(gè)是發(fā)郵件的python腳本文件。并設(shè)定一個(gè)cron定時(shí)任務(wù)。 接下來就是開始寫個(gè)shell腳本,如下(文件名:monitor_nginx_log.sh): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #!/bin/bash #日志文件路徑 logfile=/var/log/nginx #當(dāng)天日期,年月日 cur_date=`date +"%Y/%m/%d"` #開始時(shí)間(3分鐘前),時(shí)分秒 start_time=`date -d"3 minutes ago" +"%H:%M:%S"` #結(jié)束時(shí)間,時(shí)分秒 stop_time=`date +"%H:%M:%S"` #把新增的錯(cuò)誤日志寫到new_error_log中 tac $logfile/error.log | awk -v st="$start_time" -v et="$stop_time" -v dt="$cur_date" '{t=$2;t1=$1; if(dt==t1 && t>=st && t<=et) {print $0}}' > $logfile/new_error_log.txt file_size=`du $logfile/new_error_log.txt | awk '{print $1}'` #new_error_log文件大小不為0,發(fā)送郵件通知 if [[ $file_size -gt 0 ]];then echo `date +'%Y/%m/%d %H:%M:%S'`" there are new errors in nginx error.log" | cat >> /opt/mysh/monitor.log /usr/bin/python2.7 /opt/mysh/send_mail.py | tee -a /opt/mysh/monitor.log fi
ps: 上面的腳本有好幾個(gè)命令,如果有疑問的話,請(qǐng)往下看,會(huì)有解釋的
然后呢,我們還要有一個(gè)郵件發(fā)送腳本(send_mail.py),如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # -*- coding: utf-8 -*- import os import smtplib from email.header import Header from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import parseaddr, formataddr def _format_addr(s): name, addr = parseaddr(s) return formataddr((Header(name, 'utf-8').encode(), addr)) # 郵箱定義 smtp_server = 'smtp.163.com' smtp_port = 25 from_addr = 'from_addr@163.com' password = os.environ.get('MAIL_PASSWD') to_addr = 'to_addr@163.com' # 郵件對(duì)象 msg = MIMEMultipart() msg['From'] = _format_addr('發(fā)件人 <%s>' % from_addr) msg['To'] = _format_addr('收件人 <%s>' % to_addr) msg['Subject'] = Header('【Support】發(fā)現(xiàn)錯(cuò)誤', 'utf-8').encode() # 郵件正文是MIMEText: html = "<html><body><h4>檢測有錯(cuò)誤發(fā)生,詳情見附件!</h4></body></html>" msg.attach(MIMEText(html, 'html', 'utf-8')) # 添加附件 file_path = "/var/log/nginx/new_error_log.txt" attachment = MIMEApplication(open(r'file_path', 'rb').read()) attachment.add_header('Content-Disposition', 'attachment', filename="new_error_log.txt") msg.attach(attachment) # 發(fā)送郵件 print('開始發(fā)送郵件>>>') try: server = smtplib.SMTP(smtp_server, smtp_port) server.login(from_addr, password) server.sendmail(from_addr, to_addr, msg.as_string()) server.quit() except Exception, e: print "郵件發(fā)送異常:" + e finally: print('結(jié)束郵件發(fā)送<<<')
ps1: 上面的代碼中使用了os.environ.get('MAIL_PASSWD')
獲取環(huán)境變量,可以使用export MAIL_PASS=xxxx進(jìn)行設(shè)置(只對(duì)當(dāng)前shell有效,需要永久生效請(qǐng)修改/etc/profile)
ps2: 163的郵箱老是會(huì)退信,因?yàn)榘l(fā)送的次數(shù)多,而且內(nèi)容相似,會(huì)被當(dāng)成垃圾郵件。好煩~
最后我們需要做的就是在定時(shí)任務(wù)中添加一個(gè)任務(wù) 1 2 3 root@ubuntu:/opt/mysh# crontab -e # 監(jiān)控nginx錯(cuò)誤日志,每3分鐘執(zhí)行一次 */3 * * * * /opt/mysh/monitor_nginx_log.sh
ps:查看定時(shí)任務(wù)使用crontab -l
至此,我們的監(jiān)控就已經(jīng)完成了。 參考文章:Nginx日志實(shí)現(xiàn)訪問異常報(bào)警詳解
上面的shell腳本里面有幾個(gè)命令,這里簡單的解釋一下。 date命令用來獲取機(jī)器當(dāng)前時(shí)間,如果需要格式化時(shí)間,可以加號(hào)(+)傳參,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 date +'%Y-%m-%d %H:%M:%S' // 輸出格式為:2018-09-05 08:15:02 %Y表示年 %m表示月 %d表示天 %H表示小時(shí)(表示的時(shí)間是00-23) %M表示分鐘 %S表示秒 %s(表示unix時(shí)間戳的秒數(shù)) -d<字符串>:顯示字符串所指的日期與時(shí)間。字符串前后必須加上雙引號(hào); -s<字符串>:根據(jù)字符串來設(shè)置日期與時(shí)間。字符串前后必須加上雙引號(hào); -u:顯示GMT; --help:在線幫助; --version:顯示版本信息。
tac命令其實(shí)就是cat的反轉(zhuǎn)的形式,tac是從最后一行往前讀取內(nèi)容,因?yàn)槲覀兪桥袛嗍欠裼行略鲥e(cuò)誤日志,所以需要從后往前遍歷,故使用tac。
awk是行處理器,可以依次對(duì)每一行進(jìn)行處理,-v是定義變量var=value, ‘’里面內(nèi)容是引用代碼塊, $1是指第一部分內(nèi)容(空格分隔)
我們使用例子說明一下:
1 2 3 4 5 6 7 8 9 10 11 12 // 假定日志文件格式是這樣的: 2018/09/05 06:32:50 [error] 26217#26217: *597 access forbidden by rule, client: 123.123.456.456, server: localhost, request: "GET / HTTP/1.1" 2018/09/05 06:32:52 [error] 26217#26217: *597 access forbidden by rule, client: 123.123.456.456, server: localhost, request: "GET / HTTP/1.1" 2018/09/05 06:32:52 [error] 26217#26217: *597 access forbidden by rule, client: 123.123.456.456, server: localhost, request: "GET /favicon.ico HTTP/1.1" // 命令如下: awk -v st="$start_time" -v et="$stop_time" -v dt="$cur_date" '{t=$2;t1=$1; if(dt==t1 && t>=st && t<=et) {print $0}}' // 其中 -v st="$start_time" 是賦值命令,因?yàn)楹罄m(xù)有用到比較:t>=st // '{t=$2;t1=$1; if(dt==t1 && t>=st && t<=et) {print $0}}' 是引用代碼塊,此處表示匹配當(dāng)前時(shí)間段內(nèi)的內(nèi)容 // t=$2;t1=$1; 里面的 $2 是 06:32:50 這部分內(nèi)容,$1 是 2018/09/05 這部分內(nèi)容. // 因?yàn)?$ 是從1開始的,所以 $0 指的是整行的內(nèi)容。
文件內(nèi)容追加使用該命令
1 2 3 4 5 // 如:把123456追加到test.log的文件末尾,因?yàn)槭亲芳?,所以原有?nèi)容還在 echo "123456" | cat >> test.log // 注意:單個(gè)>是會(huì)覆蓋文件的,如果執(zhí)行下面命令則會(huì)覆蓋test.log原有內(nèi)容 echo "123456" | cat > test.log
讀取標(biāo)準(zhǔn)輸入的數(shù)據(jù),并將其內(nèi)容輸出成文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 輸出到標(biāo)準(zhǔn)輸出的同時(shí),保存到文件file中。如果文件不存在,則創(chuàng)建;如果已經(jīng)存在,則覆蓋 tee file // 輸出到標(biāo)準(zhǔn)輸出的同時(shí),追加到文件file中 tee -a file // 輸出到標(biāo)準(zhǔn)輸出兩次 tee - // 上面tee進(jìn)去的monitor.log是這樣的: 2018/09/07 03:27:01 there are new errors in nginx error.log 2018/09/07 03:26:57 [error] 1254#1254: *109 access forbidden by rule, client: 220.181.132.194, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "123.123.123.123" 2018/09/07 03:26:56 [error] 1254#1254: *109 access forbidden by rule, client: 220.181.132.194, server: localhost, request: "GET / HTTP/1.1", host: "123.123.123.123" 開始發(fā)送郵件>>> 結(jié)束郵件發(fā)送<<<
再補(bǔ)充2個(gè)命令:sort | uniq 1 2 3 4 5 6 7 8 9 10 11 12 13 // 命令如下: cat /var/log/nginx/error.log | awk '{print $11}' | uniq -cd | sort -nr // 執(zhí)行結(jié)果如下: 179 103.25.110.106, 178 50.63.160.242, 58 47.96.12.198, 2 194.126.182.88, 2 14.154.29.140, // 命令解釋(統(tǒng)計(jì)error.log中被禁止訪問的ip出現(xiàn)次數(shù)): uniq 用于去重,-c 統(tǒng)計(jì)重復(fù)次數(shù), -d 只顯示重復(fù)的數(shù)據(jù) sort 用于排序,-n 排序后輸出, -r 逆序排列
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào) 。