• django 多进程日志写入日志大小模式与时间切割模式


    django logging 的实现,是基于python logging模块实现,而logging模块仅仅是线程安全的;而使用uwsgi启动项目时,是启动多个进程的;logging模块并没有保证多进程安全。

    1.使用 concurrent-log-handler 包记录日志

    这个包通过加锁的方式实现了多进程安全,并且可以在日志文件达到特定大小时,分割文件,但是不支持按时间分割。如果使用日志大小来分割日志文件的话,就可以使用concurrent-log-handler。

    安装:

    pip install concurrent-log-handler

    以下为django配置文件settings.py下的配置:

    import concurrent_log_handler
    
    # 日志配置  只要将注释打开相应的日志就会写入到logs/demo.log 里面
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            # 详细
            'verbose': {
                #            错误等级    错误时间戳  错误魔抗   错误行  错误信息
                'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
            },
            # 简单
            'simple': {
                'format': '%(levelname)s:(%(asctime)s:%(module)s: %(filename)s:%(lineno)d):%(message)s'
            },
            # 错误日志格式
            'err_info': {
                'format': '%(levelname)s:(%(asctime)s:%(module)s: %(filename)s:%(lineno)d):%(message)s'
            }
        },
        # 日志过滤器
        'filters': {
            'require_debug_false': {
                '()': 'django.utils.log.RequireDebugFalse'
            },
            'require_debug_true': {
                '()': 'django.utils.log.RequireDebugTrue',
            },
        },
        'handlers': {
            'console': {
                #   过滤错误模式
                'level': 'DEBUG',
                'filters': ['require_debug_true'],
                'class': 'logging.StreamHandler',
                'formatter': 'simple'
            },
            'file': {
                # 记录日志的错误等级
                'level': 'INFO',
                # logging.handlers.RotatingFileHandler
                'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler',
                # 日志位置,日志文件名,日志保存目录必须手动创建
                'filename': os.path.join(BASE_DIR, 'logs/demo.log'),
                # 日志文件的最大值,这里我们设置100M
                'maxBytes': 1 * 1024 * 1024,
                # 日志文件的数量,设置最大日志数量为20
                'backupCount': 10,
                # 日志格式:详细格式
                'formatter': 'verbose'
            },
            # 输出info日志
            'info': {
                'level': 'INFO',
                'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler',
                'filename': os.path.join(BASE_DIR, 'logs/demo.log'),
                'maxBytes': 100 * 1024 * 1024,
                'backupCount': 10,
                'formatter': 'verbose',  # 'standard',
                'encoding': 'utf-8',  # 设置默认编码
            },
            # 专门用来记错误日志
            'error': {
                'level': 'DEBUG',
                'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler',  # 保存到文件,自动切
                'filters': ['require_debug_false'],
                'filename': os.path.join(BASE_DIR, "logs/demo.log"),  # 日志文件
                'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
                'backupCount': 10,
                'formatter': 'err_info',
                'encoding': 'utf-8',
            },
        },
        # 日志对象
        'loggers': {
            'django': {
                'handlers': ['console', 'file', 'error'],  #
                'propagate': True,  # 是否让日志信息继续冒泡给其他的日志处理系统,一般为Ture ,不保证其他第三方模块有记录
            },
            'log': {
                'handlers': ['info', 'console'],
                'level': 'INFO',
                'propagate': True
            },
            'error': {
                'handlers': ['console', 'file', 'error'],
                'level': 'DEBUG',
                'propagate': True
            },
        }
    
    }

    应用的demo:

    import logging
    
    logger = logging.getLogger('error')
    
    
    logger.error("12345678")

    这样访问这个接口,就会写入日志文件,并且messages12345678

    2.使用  TimedRotatingFileHandler记录日志

    以下为django配置文件settings.py下的配置:

    LOGGING = {
        'version': 1,
        'disable_existing_logger': False,
        'formatters': {
            'verbose': {
                'format': '%(asctime)s "%(pathname)s:%(module)s:%(funcName)s:%(lineno)d" [%(levelname)s]-%(message)s'
            },
        },
        # 处理器
        'handlers': {
            # 输出控制台
            'console': {
                'level': 'INFO',
                'class': 'logging.StreamHandler',
                'formatter': 'verbose'
            },
            # 输出文件
            'file': {
                'level': 'DEBUG',
                'class': 'logging.handlers.TimedRotatingFileHandler',
                'filename': 'logs/blog.log',
                'formatter': 'verbose',
                # 每分钟切割一次日志
                'when': 'M',
                # 时间间隔
                'interval': 1,
                # 保留5份日志
                'backupCount': 5,
                'encoding': 'utf-8'
            },
        },
        # 记录器
        'loggers': {
            'django': {
                'handlers': ['file'],  # 'console',
                'level': 'INFO',
                'propagete': True,
            },
        }
    }

    这个可以实现,按时间的间隔进行日志切割。

     

    3.使用CommonTimedRotatingFileHandler记录日志(暂时未成功)

    重写 TimedRotatingFileHandler

    通过上面的分析可以知道,出问题的点就是发生在日志分割时,一是删文件,二是没有及时更新写入句柄。

    所以针对这两点,我的对策就是:一是去掉删文件的逻辑,二是在切割文件时,及时将写入句柄更新到最新。

    代码如下:

    # 解决多进程日志写入混乱问题
    import os
    import time
    from logging.handlers import TimedRotatingFileHandler
    
    
    class CommonTimedRotatingFileHandler(TimedRotatingFileHandler):
    
        @property
        def dfn(self):
            currentTime = int(time.time())
            # get the time that this sequence started at and make it a TimeTuple
            dstNow = time.localtime(currentTime)[-1]
            t = self.rolloverAt - self.interval
            if self.utc:
                timeTuple = time.gmtime(t)
            else:
                timeTuple = time.localtime(t)
                dstThen = timeTuple[-1]
                if dstNow != dstThen:
                    if dstNow:
                        addend = 3600
                    else:
                        addend = -3600
                    timeTuple = time.localtime(t + addend)
            dfn = self.rotation_filename(self.baseFilename + "." + time.strftime(self.suffix, timeTuple))
    
            return dfn
    
        def shouldRollover(self, record):
            """
            是否应该执行日志滚动操作:
            1、存档文件已存在时,执行滚动操作
            2、当前时间 >= 滚动时间点时,执行滚动操作
            """
            dfn = self.dfn
            t = int(time.time())
            if t >= self.rolloverAt or os.path.exists(dfn):
                return 1
            return 0
    
        def doRollover(self):
            """
            执行滚动操作
            1、文件句柄更新
            2、存在文件处理
            3、备份数处理
            4、下次滚动时间点更新
            """
            if self.stream:
                self.stream.close()
                self.stream = None
            # get the time that this sequence started at and make it a TimeTuple
    
            dfn = self.dfn
    
            # 存档log 已存在处理
            if not os.path.exists(dfn):
                self.rotate(self.baseFilename, dfn)
    
            # 备份数控制
            if self.backupCount > 0:
                for s in self.getFilesToDelete():
                    os.remove(s)
    
            # 延迟处理
            if not self.delay:
                self.stream = self._open()
    
            # 更新滚动时间点
            currentTime = int(time.time())
            newRolloverAt = self.computeRollover(currentTime)
            while newRolloverAt <= currentTime:
                newRolloverAt = newRolloverAt + self.interval
    
            # If DST changes and midnight or weekly rollover, adjust for this.
            if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
                dstAtRollover = time.localtime(newRolloverAt)[-1]
                dstNow = time.localtime(currentTime)[-1]
                if dstNow != dstAtRollover:
                    if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
                        addend = -3600
                    else:  # DST bows out before next rollover, so we need to add an hour
                        addend = 3600
                    newRolloverAt += addend
            self.rolloverAt = newRolloverAt
  • 相关阅读:
    如何在github中的readme.md加入项目截图
    git图片无法正常显示
    PHP错误:SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client
    Mac安装PHP运行环境
    YII2数据库操作出现类似Database Exception – yiidbException SQLSTATE[HY000] [2002] No such file or director
    composer 安装 Yii2遇到的BUG
    js中字节B转化成KB,MB,GB
    README.md编写教程(基本语法)
    微信扫码网页登录,redirect_uri参数错误解决方法
    记录下自己亲自做的Django项目继承QQ第三方登录
  • 原文地址:https://www.cnblogs.com/hszstudypy/p/14136744.html
走看看 - 开发者的网上家园