博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Redis记录系统日志
阅读量:6068 次
发布时间:2019-06-20

本文共 4484 字,大约阅读时间需要 14 分钟。

使用Redis记录系统日志


在构建一个系统时,我们常常需要记录当前发生的事情,以及记录特定消息出现的频率,根据出现频率的高低来决定消息的排列信息,帮助我们找到重要的信息。

常见记录日志的方法有两种:

  1. 将日志记录在文件中。随时时间流逝将日志行不断添加到文件里面,并在一段时间后创建新的日志文件。这种方式为每个不同的服务创建不同的日志,由于服务轮换日志的机制不同,也缺少一种能够方便地聚合所有日志并对其进行处理的常见方法。
  2. syslog服务。这种服务几乎运行在Linux服务器和Unix服务器的514号TCP端口和UDP端口上。syslog接受其他程序发来的日志消息,并将这个消息路由至存储在硬盘上的各个日志文件,并且负责旧日志的轮换和删除工作。甚至还可以将日志消息转发给其他服务来做进一步的处理。

syslog的转发功能可以将不同的日志分别存储在同一台服务器的多个文件里面,对于长时间地记录日志非常有帮助。我们可以使用redis来存储与时间紧密相关的日志,从而在功能上替代那些需要在短期内被存储的syslog消息。

1. 最新日志

我们需要使用 “列表” 来存储最新日志文件,使用LPUSH命令将日志消息推入到列表中。如果我们之后想要查看已有日志消息的话,可以使用LRANGE命令来拉取列表中的消息。

我们还要命名不同的日志消息队列,根据问题的严重性对日志进行分级。

import timeimport loggingimport unittestimport redisfrom datetime import datetime# 设置一个字典,将大部分日志的安全级别映射为字符串SEVERITY = {    logging.DEBUG: 'debug',    logging.INFO: 'info',    logging.WARNING: 'warning',    logging.ERROR: 'error',    logging.CRITICAL: 'critical',}SEVERITY.update((name, name) for name in SEVERITY.values())"""存储最新日志文件,命名不同的日志消息队列,根据问题的严重性对日志进行分级@param {object}@param {string} name    消息队列名称@param {string} message 消息@param {string} severity安全级别@param {object} pip     pipline"""def logRecent(conn, name, message, severity=logging.INFO, pip=None):    # 将日志的安全级别转换为简单的字符串    severity = str(SEVERITY.get(severity, severity)).lower()    # 创建要保存的redis列表key    destination = 'recent:%s:%s'%(name, severity)    # 将当前时间加到消息里面,用于记录消息的发送时间    message = time.asctime() + ' ' + message    # 使用流水线来将通信往返次数降低为一次    pipe = pip or conn.pipeline()    # 将消息添加到列表的最前面    pipe.lpush(destination, message)    # 修剪日志列表,让它只包含最新的100条消息    pipe.ltrim(destination, 0, 99)    pipe.execute()

2. 常见日志

我们需要记录较高频率出现的日志,使用“有序集合”,将消息作为成员,消息出现的频率为成员的分值。

为了确保我们看到的常见消息都是最新的,需要以每小时一次的频率对消息进行轮换,并在轮换日志的时候保留上一个小时记录的常见消息,从而防止没有任何消息存储的情况出现。

"""记录较高频率出现的日志,每小时一次的频率对消息进行轮换,并在轮换日志的时候保留上一个小时记录的常见消息@param {object}@param {string} name    消息队列名称@param {string} message 消息@param {string} severity安全级别@param {int}    timeout 执行超时时间"""def logCommon(conn, name, message, severity=logging.INFO, timeout=5):    # 设置日志安全级别    severity = str(SEVERITY.get(severity, severity)).lower()    # 负责存储近期的常见日志消息的键    destination = 'common:%s:%s'%(name, severity)    # 每小时需要轮换一次日志,需要记录当前的小时数    start_key = destination + ':start'    pipe = conn.pipeline()    end = time.time() + timeout    while time.time() < end:        try:            # 对记录当前小时数的键进行监听,确保轮换操作可以正常进行            pipe.watch(start_key)            # 当前时间            now = datetime.utcnow().timetuple()            # 取得当前所处的小时数            hour_start = datetime(*now[:4]).isoformat()            existing = pipe.get(start_key)            # 开始事务            pipe.multi()            # 如果这个常见日志消息记录的是上个小时的日志            if existing and existing < hour_start:                # 将这些旧的常见日志归档                pipe.rename(destination, destination + ':last')                pipe.rename(start_key, destination + ':pstart')                # 更新当前所处的小时数                pipe.set(start_key, hour_start)            elif not existing:                pipe.set(start_key, hour_start)            # 记录日志出现次数            pipe.zincrby(destination, message)            # 将日志记录到日志列表中,调用excute            logRecent(pipe, name, message, severity, pipe)            return        except redis.exceptions.WatchError:            continue

测试

测试代码如下:

class TestLog(unittest.TestCase):    def setUp(self):        import redis        self.conn = redis.Redis(db=15)        self.conn.flushdb    def tearDown(self):        self.conn.flushdb()        del self.conn        print        print    def testLogRecent(self):        import pprint        conn = self.conn        print "Let's write a few logs to the recent log"        for msg in xrange(5):            logRecent(conn, 'test', 'this is message %s'%msg)        recent = conn.lrange('recent:test:info', 0, -1)        print 'The current recent message log has this many message:', len(recent)        print 'Those message include:'        pprint.pprint(recent[:10])        self.assertTrue(len(recent) >= 5)    def testLogCommon(self):        import pprint        conn = self.conn        print "Let's writ a few logs to the common log"        for count in xrange(1, 6):            for i in xrange(count):                logCommon(conn, 'test', 'message-%s'%count)        common = conn.zrevrange('common:test:info', 0, -1, withscores=True)        print 'The current common message log has this many message:', len(common)        print 'Those common message include:'        pprint.pprint(common)        self.assertTrue(len(common) >= 5)if __name__ == '__main__':    unittest.main()

转载地址:http://vxfgx.baihongyu.com/

你可能感兴趣的文章
Servlet系列文章(一)
查看>>
点击文件下载
查看>>
纯java环境下sqlsqlcipher解密sqlite数据库文件
查看>>
思科路由器基本命令学习总结
查看>>
Mysql 5.7.9源代码安装
查看>>
【表单设计】input的size和maxlength属性
查看>>
存储方案与存储产品之DAS篇
查看>>
Solr1.3的核心机制
查看>>
vue 按需加载
查看>>
OpenCV编程->ROI区域保存为图片
查看>>
SS哥的crontab教程
查看>>
python 面向对象
查看>>
兼职议会
查看>>
2012.12.18
查看>>
rpm包管理和yum的使用
查看>>
Linux tmux
查看>>
在此处打开命令窗口问题
查看>>
中国红××××××C
查看>>
深度探索I/O完成端口
查看>>
年夏天针对中国市场推出廉价版的iPhone—iPhone Mini
查看>>