python模块,邮件推送交换机error级别以上log,django前端展示
个人博客地址
前情概要
原syslog服务器只收集不推送日志,可以实时展示,服务器在海外内网,办公网做的有分流,到日志服务器的流量送到香港,其余流量国内,疫情期间在家办公,每次连接需要拨海外l2tp,挂着梯子访问国内时延较大影响办公,所以日志服务器基本一天登录一次,然后就错过了重要的error信息-_-!,所以在家的几天做了两个方案,1是上篇博客中的数通技术,2就是做一个功能邮箱推送error级别以上的日志
功能
django前端用以日志的展示和搜索,后端用于日志采集与邮件推送
目录结构
-swlog
---swlog
------ setting.py
------ url.py
---log
------ models.py
------ views.py
------ limit.py
---sock
------ bin.py
------ formstr.py
------ sendmail.py
------ sql.py
---templates
------ log.html
------ login.html
urls
1 from django.contrib import admin 2 from django.urls import path 3 from log import views 4 import threading 5 from log.sock.bin import logserver 6 urlpatterns = [ 7 path('admin/', admin.site.urls), 8 path('login.html/', views.login), 9 path('log.html/', views.log), 10 ] 11 t1 = threading.Thread(target=logserver) 12 t1.start()
后端分页
1 class limit: 2 def limit(self,res,obj): 3 limit = int(res.GET.get('limit',1)) 4 pagelimit = int(res.GET.get('pagelimit',50)) 5 startlimit = (limit-1)*pagelimit 6 endlimit = limit*pagelimit 7 logdb = obj[startlimit:endlimit] 8 page_count,lastpage_count = divmod(obj.count(),pagelimit) 9 if lastpage_count: 10 page_count +=1 11 startpage = 1 12 endpage = page_count 13 hrefli=[] 14 hrefli.append('<form style="display: inline;" method="GET" action="%s">每页显示<select name="pagelimit"><option>30</option><option>50</option><option>100</option><option>300</option><option>500</option></select><input type="submit" value = "确定"></form>'%res.path) 15 if limit !=1: 16 hrefli.append('<a class="page" href="%s?limit=%s">%s</a>'%(res.path,limit-1,'上一页')) 17 for x in range(startpage,endpage+1): 18 if limit+3 < x or x <limit -3: 19 hrefli.append('<a class="page pitch hidden" href="%s?limit=%s">%s</a>'%(res.path,x,x)) 20 elif x == limit: 21 hrefli.append('<a class="page pitch" href="%s?limit=%s">%s</a>'%(res.path,x,x)) 22 else: 23 hrefli.append('<a class="page unpitch" href="%s?limit=%s">%s</a>'%(res.path,x,x)) 24 if limit != endpage: 25 hrefli.append('<a class="page" href="%s?limit=%s">%s</a>'%(res.path,limit+1,'下一页')) 26 href=''.join(hrefli) 27 return href,logdb
view后端进行登录验证,拉取日志
1 from django.shortcuts import render,redirect,HttpResponse 2 from log import models 3 from log.limit import limit 4 from django.views.decorators.csrf import csrf_exempt 5 from functools import wraps 6 from django.utils.safestring import mark_safe 7 def auth(func): 8 @wraps(func) 9 def check_login(res,*args,**kwargs): 10 try: 11 res.session['name'] 12 return func(res,*args,**kwargs) 13 except: 14 return render(res,'login.html') 15 16 return check_login 17 18 19 @csrf_exempt 20 def login(res): 21 if res.method == 'GET': 22 return render(res,'login.html') 23 elif res.method == 'POST': 24 username = res.POST.get('username') 25 passwd = res.POST.get('passwd') 26 if models.login.objects.filter(username=username,passwd=passwd): 27 res.session.set_expiry(3600) 28 res.session['name']=username 29 30 return redirect('/log.html') 31 else: 32 error = '用户名或密码错误' 33 return render(res,'login.html',{'error':error}) 34 35 @auth 36 @csrf_exempt 37 def log(res): 38 obj = models.log.objects 39 if res.method =='GET': 40 pagelimit = limit() 41 html_lable,logdb = pagelimit.limit(res,obj.all()) 42 return render(res,'log.html',{'logdb':logdb,'limit':mark_safe(html_lable)}) 43 elif res.method == 'POST': 44 if res.POST.get('search'): 45 from django.db.models import Q 46 data = res.POST.get('search') 47 logdb = obj.filter(Q(time__contains=data)| Q(host__contains=data)| Q(level__contains=data)| Q(message__contains=data))[0:500] 48 return render(res,'log.html',{'logdb':logdb}) 49 elif res.POST.get('day'): 50 import time 51 day = int(res.POST.get('day'))-1 52 h_time = time.strftime('%Y-%m-%d',time.localtime(time.time()-86400*day)) 53 delete_count = obj.filter(time__lt=h_time).count() 54 obj.filter(time__lt=h_time).delete() 55 return redirect('/log.html') 56 return redirect('/log.html') 57 58
templates前端简单做两个页面,一个用于登录,另一个用于展示和搜索
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>登录</title> 8 <style> 9 .input{width: 300px; height: 20px;margin-top: 10px;} 10 </style> 11 </head> 12 <body style="background-color: royalblue;"> 13 14 <div style="position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);"> 15 16 17 <form action="/login.html/" method="POST"> 18 <h1 style="text-align: center; color: seashell;">日志管理系统</h1> 19 <input class="input" name="username" type="text" placeholder=username> 20 <br> 21 <input class="input" name="passwd" type="password" placeholder=passwd> 22 <br> 23 <button style="width: 300px;height: 30px; margin-top: 10px;" >登录</button> 24 <span>{{error}}</span> 25 </form> 26 </div> 27 </body> 28 29 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <meta http-equiv="Refresh" content="60" > 8 <title>Document</title> 9 <style> 10 *{margin: 0;padding: 0;} 11 .top{position: fixed;top: 0px;height: 100px;width: 100%;z-index: 100;background-color: blanchedalmond;} 12 .search{float: right;margin-right: 50px;} 13 .error,.crit,.alert,.emerg{background-color: rgb(243, 67, 67);} 14 .debug,.notice,.info{background-color: cornflowerblue;} 15 .warning{background-color: yellow;} 16 td {white-space:nowrap;} 17 body{background-color:whitesmoke;} 18 .hidden{display:none ;} 19 .pitch{background-color:black;color: white;} 20 .unpitch{background-color: white;color: black;} 21 a{margin-left: 10px;} 22 a:hover{background-color: cornflowerblue;} 23 </style> 24 25 </head> 26 <body> 27 <div class="top" > 28 <h3 style="text-align: center;">日志管理系统</h3> 29 <form action="/log.html/" method="POST" class="search"> 30 <input type="text" id="search" name="search"> 31 <span> 32 <input type="submit" value="搜索"> 33 </span> 34 </form> 35 36 {{limit}} 37 <form action="/log.html/" method="POST" style="margin-top:10px"> 38 <span>清除</span> 39 <select name="day"> 40 <option>3</option> 41 <option>7</option> 42 <option>15</option> 43 <option>30</option> 44 </select> 45 <span>天历史数据</span> 46 <input type="submit" value="确定"> 47 </form> 48 49 </div> 50 <div class = 'message'> 51 <table border="1px" style="width: 100%;"> 52 <tr> 53 <td>time</td> 54 <td>host</td> 55 <td>level</td> 56 <td>message</td> 57 </tr> 58 59 {%for mess in logdb%} 60 <tr class = {{mess.level}}> 61 <td>{{mess.time}}</td> 62 <td>{{mess.host}}</td> 63 <td>{{mess.level}}</td> 64 <td>{{mess.message}}</td> 65 </tr> 66 {%endfor%} 67 68 </table> 69 70 </div> 71 </body> 72 </html>
日志采集与邮件推送模块
1 import pymysql,time 2 class db: 3 def __init__(self): 4 self.conn = pymysql.connect(host='127.0.0.1',port = 3306,user = 'root',passwd = 'd****', db='log') 5 self.coursor = self.conn.cursor() 6 def write(self,host,level,message): 7 ltime = time.strftime('%Y-%m-%d',time.localtime(time.time())) 8 sql = 'insert into swlog_log(host,level,message,time) values("%s","%s","%s","%s")'%(host,level,message,ltime) 9 self.coursor.execute(sql) 10 self.conn.commit() 11 self.conn.close()
1 import email 2 import smtplib 3 from email.header import Header 4 from email.utils import formataddr 5 from email.mime.text import MIMEText 6 class sendemail(): 7 def __init__(self,email_list,content,subject): 8 self.email_list = email_list 9 self.content = content 10 self.subject = subject 11 def sendemail(self): 12 msg = MIMEText(self.content,'plain','utf-8') 13 msg['from'] = formataddr(['dark','97****@qq.com']) 14 msg['subject'] = self.subject 15 service = smtplib.SMTP('smtp.qq.com') 16 service.login('97****@qq.com','ilz****') 17 service.sendmail('97****@qq.com',self.email_list,msg.as_string()) 18 service.quit()
1 import re 2 def syslog(date): 3 date = date.replace('\"','\'') 4 pri = re.findall('\d+',date)[0] 5 pri = int(pri) 6 level = re.findall('<\d+>',date)[0] 7 date = date.replace(level,'').strip() 8 if pri%8 == 0: 9 div_class = 'emerg' 10 elif pri%8 == 1: 11 div_class = 'alert' 12 elif pri%8 == 2: 13 div_class = 'crit' 14 elif pri%8 == 3: 15 div_class = 'error' 16 elif pri%8 == 4: 17 div_class = 'warning' 18 elif pri%8 == 5: 19 div_class = 'notice' 20 elif pri%8 == 6: 21 div_class = 'info' 22 elif pri%8 == 7: 23 div_class ='debug ' 24 return date,div_class
1 import socket,re,os,subprocess 2 from log.sock import formstr,sql 3 from log.sock.sendmail import sendemail 4 import time 5 6 def logserver(): 7 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 8 address = ('10.0.64.61',514) 9 s.bind(address) 10 mail_level = ['error','crit','alert','emerg'] 11 while True: 12 data,address = s.recvfrom(10240) 13 data = data.decode(encoding='utf8') 14 data,div_class = formstr.syslog(data) 15 write_db = sql.db() 16 write_db.write(address[0],div_class,data) 17 if div_class in mail_level: 18 title = '主机%s,严重等级%s'%(address[0],div_class) 19 mail = sendemail(['cs11241991@163.com','dark@lonlife-inc.com'],data,title) 20 mail.sendemail()
功能测试
以驱魔为理想,为生计而奔波