Python实验日志11-经典协议-监控Syslog Trap再对Syslog进行基本分词写入数据库并分析

菜鸟记录自己的python成长历程

使用Python监控Syslog Trap!监控OSPF邻居状态!再对Syslog进行基本分词(与上课相同)写入数据库并分析

最终结果如下图, PythonOSPF邻居状态改变的Syslog Trap进行分析,并打印!

读取数据库!产生SYSLOG严重级别分布图

读取数据库!产生SYSLOG日志源分布图

 

这实验为上次实验的升级版:https://blog.csdn.net/u012092175/article/details/114275206

直接进入正题

一:思路

1:对于监控syslog trap,首先得确认在监控设备端需要发起syslog信息到服务器。

2:不过由于送snmp变成了syslog,也就是说,处理数据更加的“人性化”了,不再是人类难以理解的代码了。

syslog举例:<189>188: *Mar 3 13:14:33: %OSPF-5-ADJCHG: Process 1, Nbr 2.2.2.2 on GigabitEthernet4 from FULL to DOWN, Neighbor Down: Interface down or detached

我们需要做的只需要监控syslog的端口,收到数据后对数据进行分词。

3:对于需求:由于需要输出状态,所以我们需要判断ospf的log信息

4:将获取的syslog进行分词,批量的写入数据库中

5:读取数据库,再使用matplotlib模块创造饼状图

 

二:实现

1:对于监控syslog trap,首先得确认在监控设备端需要发起syslog信息到服务器。

参考:python基础之socket与socketserver - 招财大龙猫 - 博客园 https://www.cnblogs.com/hls91/p/11714827.html

1.1需要使用到的模块如下:

import socketserver   # 做服务器
import re   # 做分词匹配
from dateutil import parser   # 日期
import os   # 系统内独写文件等操作
import sqlite3   # 数据库

1.2:定义服务器监听的处理方法:

class SyslogUDPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = bytes.decode(self.request[0].strip())  # 读取数据
        print(data)

1.3:启动服务器

def syslog_server(HOST = '0.0.0.0',PORT = int(514)):
    try:
        server = socketserver.UDPServer((HOST, PORT), SyslogUDPHandler)  # 绑定本地地址,端口和syslog处理方法
        print("Syslog 服务已启用, 写入日志到数据库!!!")
        server.serve_forever(poll_interval=0.5)  # 运行服务器,和轮询间隔
    except (IOError, SystemExit): # 奔溃则重新启动
        raise

1.4:网络环境与之前相同,CSR配置了syslog

CSR syslog 设定:
logging source-interface GigabitEthernet1
logging host 192.168.6.222
logging trap 6 # 默认为6

1.5:触发syslog,查看一下效果

 

 

 

2:我们需要做的只需要监控syslog的端口,收到数据后对数据进行分词。

通过假设syslog服务器,已经能够正常的收到syslog日志信息,次是我们观察一下syslog的格式,将他进行格式化处理

以关键词:%OSPF-5-ADJCHG 作为触发点,使用re模块通过正则表达式来匹配需要的内容。

为方便理解,可以查看以下对比

<189>188: *Mar 3 13:14:33: %OSPF-5-ADJCHG: Process 1, Nbr 2.2.2.2 on GigabitEthernet4 from FULL to DOWN, Neighbor Down: Interface down or detached

           .*             %OSPF-5-ADJCHG: Process (\d+), Nbr (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}) on (\w+\d+) from (\w+) to (\w+),       .*

一共5个元素,既0~4;

 

3:输出状态,所以我们需要判断ospf的log信息:

3.1 代码实现

# <189>188: *Mar  3 13:14:33: %OSPF-5-ADJCHG: Process 1, Nbr 2.2.2.2 on GigabitEthernet4 from FULL to DOWN, Neighbor Down: Interface down or detached
        if 'OSPF-5-ADJCHG' in str(data):
            ospf_info = re.match(r'.*%OSPF-5-ADJCHG: Process (\d+), Nbr (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}) on (\w+\d+) from (\w+) to (\w+),.*',str(data)).groups()
            print(f'OSPF process {ospf_info[0]} Neighbor {ospf_info[1]} status {ospf_info[4]}')

4:将获取的syslog进行分词,批量的写入数据库中

4.1 写入数据库之前,我们需要设计数据库的表项(自己做分词挺麻烦的,后期可以用ELK来做,更加的简单方便),

syslog的facility对应关系,可以参考:https://www.geek-share.com/detail/2659540658.html
cisco-syslog-severity-levels:0 – Emergency  1 – Alert  2 – Critical  3 – Error  4 – Warning  5 – Notice  6 – Informational  7 – Debug

在本次模板的syslog中,我们以以下日志信息为例

<187>83: *Apr  4 00:03:12.969: %LINK-3-UPDOWN: Interface GigabitEthernet2, changed state to up

以下为备注说明:

['facility'] = 187 >> 3 = 23 # 前5位为facility,所以向右移位操作>>3获取前5位
['facility_name'] = 23 对应 local7
['logid'] = 83
['time'] = 00:03:12.969
['log_source'] = LINK
['severity_level'] = 3
['severity_level_name'] = error
['description'] = UPDOWN
['text'] = Interface GigabitEthernet2, changed state to up

补充说明:

<187>83: *Apr 4 00:03:12.969: %LINK-3-UPDOWN: Interface GigabitEthernet2, changed state to up

187之所以做位移是因为:187 转 二进制 = 1011 1011

二进制其中前5位代表的是facility既1011 1,后三位代表的是优先级011

将1011 1填充至8位后 = 0001 0111 = 23

将011填充至8位后 = 0000 0011 = 3

对应facility = 23 = Local7   ;  severity_level 对应 %LINK-3-UPDOWN = 3

定义facility,severity_level

# facility与ID的对应关系的字典(手动定义)
facility_dict = {0: 'KERN',
                 1: 'USER',
                 2: 'MAIL',
                 3: 'DAEMON',
                 4: 'AUTH',
                 5: 'SYSLOG',
                 6: 'LPR',
                 7: 'NEWS',
                 8: 'UUCP',
                 9: 'CRON',
                 10: 'AUTHPRIV',
                 11: 'FTP',
                 16: 'LOCAL0',
                 17: 'LOCAL1',
                 18: 'LOCAL2',
                 19: 'LOCAL3',
                 20: 'LOCAL4',
                 21: 'LOCAL5',
                 22: 'LOCAL6',
                 23: 'LOCAL7'}

# severity_level与ID的对应关系的字典
severity_level_dict = {0: 'EMERG',
                       1: 'ALERT',
                       2: 'CRIT',
                       3: 'ERR',
                       4: 'WARNING',
                       5: 'NOTICE',
                       6: 'INFO',
                       7: 'DEBUG'}

分词代码如下:

        syslog_info_dict = {'device_ip': self.client_address[0]} # 记录client的IP
        try:            
            syslog_info = re.match(r'^<(\d*)>(\d*): \*(.*): %(\w+)-(\d)-(\w+): (.*)', str(data)).groups() # 通过正则匹配需要的内容
            # 后3位为severity_level  & 0b111 获取后3位
            syslog_info_dict['facility'] = (int(syslog_info[0]) >> 3)
            syslog_info_dict['facility_name'] = facility_dict[int(syslog_info[0]) >> 3]
            syslog_info_dict['logid'] = int(syslog_info[1])
            syslog_info_dict['time'] = parser.parse(syslog_info[2])
            syslog_info_dict['log_source'] = syslog_info[3]
            syslog_info_dict['severity_level'] = int(syslog_info[4])
            syslog_info_dict['severity_level_name'] = severity_level_dict[int(syslog_info[4])]
            syslog_info_dict['description'] = syslog_info[5]
            syslog_info_dict['text'] = syslog_info[6]
        except AttributeError:
            # 有些日志会缺失%SYS-5-CONFIG_I, 造成第一个正则表达式无法匹配 , 也无法提取severity_level
            # 下面的icmp的debug就是示例
            # <191>91: *Apr  4 00:12:29.616: ICMP: echo reply rcvd, src 10.1.1.80, dst 10.1.1.253, topology BASE, dscp 0 topoid 0
            syslog_info = re.match(r'^<(\d*)>(\d*): \*(.*): (\w+): (.*)', str(data)).groups()
            print(syslog_info[0])
            syslog_info_dict['facility'] = (int(syslog_info[0]) >> 3)
            syslog_info_dict['facility_name'] = facility_dict[int(syslog_info[0]) >> 3]
            syslog_info_dict['logid'] = int(syslog_info[1])
            syslog_info_dict['time'] = parser.parse(syslog_info[2])
            syslog_info_dict['log_source'] = syslog_info[3]
            # 如果在文本部分解析不了severity_level, 切换到syslog_info[0]去获取
            # 185 二进制为 1011 1001
            # 前5位为facility  >> 3 获取前5位
            # 后3位为severity_level  & 0b111 获取后3位
            syslog_info_dict['severity_level'] = (int(syslog_info[0]) & 0b111)
            syslog_info_dict['severity_level_name'] = severity_level_dict[(int(syslog_info[0]) & 0b111)]
            syslog_info_dict['description'] = 'N/A'
            syslog_info_dict['text'] = syslog_info[4]

链接并将syslog分词后写入数据库代码如下:

       conn = sqlite3.connect(gl_dbname)
        cursor = conn.cursor()
        cursor.execute("insert into syslogdb (time, \
                                              device_ip, \
                                              facility, \
                                              facility_name, \
                                              severity_level, \
                                              severity_level_name, \
                                              logid, \
                                              log_source, \
                                              description, \
                                              text) values ('%s', '%s', %d, '%s', %d, '%s', %d, '%s', '%s', '%s')" % (syslog_info_dict['time'],
                                                                                                                      syslog_info_dict['device_ip'],
                                                                                                                      syslog_info_dict['facility'],
                                                                                                                      syslog_info_dict['facility_name'],
                                                                                                                      syslog_info_dict['severity_level'],
                                                                                                                      syslog_info_dict['severity_level_name'],
                                                                                                                      syslog_info_dict['logid'],
                                                                                                                      syslog_info_dict['log_source'],
                                                                                                                      syslog_info_dict['description'],
                                                                                                                      syslog_info_dict['text'],
                                                                                                                      ))
        conn.commit()

实验环境下创建数据库代码:

    gl_dbname = 'syslog.sqlite'
    # 实验环境,检测是否存在数据库,如果存在,则删除数据库,重新再创建一个空的数据库
    if os.path.exists(gl_dbname):
        os.remove(gl_dbname)
    # 连接数据库
    conn = sqlite3.connect(gl_dbname)
    cursor = conn.cursor()
    # 创建数据库
    cursor.execute("create table syslogdb(id INTEGER PRIMARY KEY AUTOINCREMENT,\
                                         time timestamp, \
                                         device_ip varchar(32),\
                                         facility int,\
                                         facility_name varchar(32),\
                                         severity_level int,\
                                         severity_level_name varchar(32),\
                                         logid int,\
                                         log_source varchar(32), \
                                         description varchar(128), \
                                         text varchar(1024)\
                                         )")
    conn.commit()

5:读取数据库,再使用matplotlib模块创造饼状图

5.1 对于用matplotlib做图表,实际上我们可以通过JavaScript来做,但是其实还可以用更加方便快捷的Kibana来实现,具体也是后续ELK的内容,暂时不管

创建饼状图就按照以下固定参数进行吧,感觉没什么特别需要记录的

以下为plt的处理方法:

import sqlite3
from dateutil import parser
from matplotlib import pyplot as plt
from practice_syslog_server_to_db import severity_level_dict

def syslog_show2(name_list, count_list, show_name):
    plt.rcParams['font.sans-serif'] = ['Simhei']  # 设置中文
    plt.figure(figsize=(6, 6))# 调节图形大小,宽,高
    # 使用count_list的比例来绘制饼图
    # 使用level_list作为注释
    patches, l_text, p_text = plt.pie(count_list,
                                      labels=name_list,
                                      labeldistance=1.1,
                                      autopct='%3.1f%%',
                                      shadow=False,
                                      startangle=90,
                                      pctdistance=0.6)
    '''
        # labeldistance,文本的位置离远点有多远,1.1指1.1倍半径的位置
        # autopct,圆里面的文本格式,%3.1f%%表示小数有三位,整数有一位的浮点数
        # shadow,饼是否有阴影
        # startangle,起始角度,0,表示从0开始逆时针转,为第一块。一般选择从90度开始比较好看
        # pctdistance,百分比的text离圆心的距离
        # patches, l_texts, p_texts,为了得到饼图的返回值,p_texts饼图内部文本的,l_texts饼图外label的文本
    '''
    # 改变文本的大小
    # 方法是把每一个text遍历。调用set_size方法设置它的属性
    for t in l_text:
        t.set_size = 30
    for t in p_text:
        t.set_size = 20
    # 设置x,y轴刻度一致,这样饼图才能是圆的
    plt.axis('equal')
    plt.title(show_name)  # 设定主题,show_name是输入的元素
    plt.legend()
    plt.show()

5.2 接下来就是连接数据库,读取需要的信息,统计数量并且生成表格

    # 连接数据库
    conn = sqlite3.connect('syslog.sqlite')
    cursor = conn.cursor()

5.3 获取严重级别分布图 

重点是需要知道数据库的语法,让它输出名称,以及统计名称对应的数量。

    # 获取严重级别
    cursor.execute("select severity_level_name,COUNT(*) as count from syslogdb group by severity_level_name")
    yourresults = cursor.fetchall()
    severity_level_name_list = []
    severity_level_count_list = []
    for severity_level_name_count in yourresults:
        severity_level_name_list.append(severity_level_name_count[0])
        severity_level_count_list.append(severity_level_name_count[1])
    syslog_show2(severity_level_name_list, severity_level_count_list, 'SYSLOG严重级别分布图')

5.4 日志源分布图

    cursor.execute("select log_source,COUNT(*) as count from syslogdb group by log_source")
    yourresults = cursor.fetchall()
    facility_name_name_list = []
    facility_name_count_list = []
    for facility_name_name_count in yourresults:
        facility_name_name_list.append(facility_name_name_count[0])
        facility_name_count_list.append(facility_name_name_count[1])
    syslog_show2(facility_name_name_list, facility_name_count_list, '日志源分布图')

6:效果展示

6.1 获取严重级别分布图

6.2 日志源分布图

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ImTheRx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值