• python logging 实现的进程安全的文件回滚日志类


      python标准库中的logging模块在记录日志时经常会用到,但在实际使用发现它自带的用于本地日志回滚的类

    logging.handlers.RotatingFileHandler 在多进程环境下会出现不同进程向不同文件写的问题,原因就是在当前

    日志文件写满后回滚的时候没有处理好并发问题(或者可以说基本没处理),因此自己实现了一个相似功能的类,

    本来是打算使用多进程锁,写完后发现没啥用。。。,于是就新建了一个.lock文件作为锁来处理多进程。用文件的

    修改时间来控制只能有一个进程访问。

      代码如下:

    #coding=utf-8
    import os
    import sys
    import json
    import time
    import logging
    import traceback
    import logging.handlers
    from multiprocessing import Lock
    
    
    class SpiderRotatingFileHandler(logging.handlers.RotatingFileHandler):
        u'''
        文件回滚日志处理器
        特点:
            1. 利用备份文件修改时间做判断 修复了多进程下同时多个日志文件被写入的bug
            2. 可选项 使用json格式记录日志文件
        
        '''
        def __init__(self, filename, mode='a', maxBytes=0,
                     backupCount=0, encoding=None, delay=0, is_json=False):
            logging.handlers.RotatingFileHandler.__init__(self,
                filename, mode, maxBytes, backupCount, encoding, delay)
            # 格式处理器
            self.Formatter = logging.Formatter()
            # 进程锁
            self.my_lock = Lock()
    
            self.is_json = is_json
            if self.is_json:
                self.format = self.json_format
    
        def json_format(self, record):
            u'''
            json 格式化日志
            @record: 日志记录对象
            type: logging.LogRecord
            '''
            # 增加 asctime 属性
            record.asctime = self.Formatter.formatTime(record)
            #
            message = record.getMessage()
            log_data = {}
            # 检查是否为json格式 并且是字典形式
            try:
                log_data = json.loads(message)
                if not isinstance(log_data, dict):
                    log_data = {}
            except Exception as e:
                exc_info = traceback.format_exc()
                #sys.stderr.write(exc_info)
    
            # 获取日志基本信息
            log_record_basic_fields = [
                "levelname", "filename", "lineno",
                "name", "created", "asctime",
                ]
    
            if not log_data:
                log_data.update({
                    "_message": message,
                    })
    
            for attr in log_record_basic_fields:
                value = getattr(record, attr, "")
                log_data.update({
                    "_{}".format(attr): value,
                    })
            try:
                result = json.dumps(log_data, ensure_ascii=False)
            except:
                result = json.dumps(log_data)
            return result
    
        def doRollover(self):
            """
            Do a rollover, as described in __init__().
            """
            with self.my_lock:
                if self.stream:
                    self.stream.close()
                    self.stream = None
                lock_file = "%s.lock"%self.baseFilename
                max_modify_interval = 3 # seconds
                do_flag = 0
                
                # 利用 Lock 文件被修改时间保证不会出现同时多个文件被写入
                if not os.path.exists(lock_file):
                    with open(lock_file, "w"):
                        pass
                    do_flag = 1
                elif time.time() - os.stat(lock_file).st_mtime > max_modify_interval:
                    do_flag = 1
                else:
                    pass
                if do_flag:
                    for i in range(self.backupCount - 1, 0, -1):
                        sfn = "%s.%d" % (self.baseFilename, i)
                        dfn = "%s.%d" % (self.baseFilename, i + 1)
                        if os.path.exists(sfn):
                            # 删除最大备份文件
                            if os.path.exists(dfn):
                                os.remove(dfn)
                            os.rename(sfn, dfn)
                            
                    dfn = self.baseFilename + ".1"
                    if os.path.exists(dfn):
                        os.remove(dfn)
                        
                    if os.path.exists(self.baseFilename):
                        os.rename(self.baseFilename, dfn)
                    # 刷新 Lock 文件修改时间
                    with open(lock_file, "w"):
                        pass
    
            if not self.delay:
                self.stream = self._open()
            return

      经过测试后发现,日志文件不再出现混乱写入(不过总感觉 3 秒好像还会出现点问题,万一在3秒内写满了日

    志文件可能会造成日志文件大小超过限制。)

      json格式的日志输出算是附加的功能吧

      ok,欢迎找茬

  • 相关阅读:
    FFmpeg简单使用:解封装 ---- 基本流程
    SDL播放PCM音频数据
    JDK8时间新API-2
    RocketMq延时队列的实现原理
    Kibana复杂查询语句
    Es基础api
    Redis sscan命令
    如何实现分布式的延时队列
    客户端从broker拉取的messagequeue的样子
    RocketMq多个consumerQueue长什么样子
  • 原文地址:https://www.cnblogs.com/dyfblog/p/6419165.html
Copyright © 2020-2023  润新知