zoukankan      html  css  js  c++  java
  • 修复日志,阻止给日志多次添加handlers时候重复打印的问题

    1、解决如果多次添加handlers重复打印的问题。在__add_handlers方法中作出判断。

     2、由get_logger_and_add_handlers和get_logger_without_handlers返回logger类型的实例,不再使用代理模式在本类里面添加debug info  warning erro critical方法,解决日志fomatter的lineno显示的是本类的相关方法的行数,而不是具体打印日志地方的代码行数。

    # coding=utf8
    """
    @author bfzs
    """
    import os
    import unittest
    import logging
    from logging.handlers import RotatingFileHandler
    
    if os.name == 'posix':
        from cloghandler import ConcurrentRotatingFileHandler
    
    format_dict = {
        1: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
        2: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
        3: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
        4: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
        5: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
    }
    
    
    class LogLevelException(Exception):
        def __init__(self, log_level):
            err = '设置的日志级别是 {0}, 设置错误,请设置为1 2 3 4 5 范围的数字'.format(log_level)
            Exception.__init__(self, err)
    
    
    class LogManager(object):
        """
        一个日志类,用于创建和捕获日志,支持将日志打印到控制台打印和写入日志文件。
        """
    
        def __init__(self, logger_name=None):
            """
            :param logger_name: 日志名称,当为None时候打印所有日志
            """
            self.logger = logging.getLogger(logger_name)
            self._logger_level = None
            self._is_add_stream_handler = None
            self._log_path = None
            self._log_filename = None
            self._log_file_size = None
            self._formatter = None
    
        def get_logger_and_add_handlers(self, log_level_int=1, is_add_stream_handler=True, log_path=None, log_filename=None, log_file_size=10):
            """
           :param log_level_int: 日志输出级别,设置为 1 2 3 4 5,分别对应输出DEBUG,INFO,WARNING,ERROR,CRITICAL日志
           :param is_add_stream_handler: 是否打印日志到控制台
           :param log_path: 设置存放日志的文件夹路径
           :param log_filename: 日志的名字,仅当log_path和log_filename都不为None时候才写入到日志文件。
           :param log_file_size :日志大小,单位M,默认10M
           :type logger_name :str
           :type log_level_int :int
           :type is_add_stream_handler :bool
           :type log_path :str
           :type log_filename :str
           :type log_file_size :int
           """
            self.__check_log_level(log_level_int)
            self._logger_level = self.__transform_logger_level(log_level_int)
            self._is_add_stream_handler = is_add_stream_handler
            self._log_path = log_path
            self._log_filename = log_filename
            self._log_file_size = log_file_size
            self._formatter = format_dict[log_level_int]
            self.__set_logger_level()
            self.__add_handlers()
            return self.logger
    
        def get_logger_without_handlers(self):
            """返回一个不带hanlers的logger"""
            return self.logger
    
        def __set_logger_level(self):
            self.logger.setLevel(self._logger_level)
    
        @staticmethod
        def __check_log_level(log_level_int):
            if log_level_int not in [1, 2, 3, 4, 5]:
                raise LogLevelException(log_level_int)
    
        @staticmethod
        def __transform_logger_level(log_level_int):
            logger_level = None
            if log_level_int == 1:
                logger_level = logging.DEBUG
            elif log_level_int == 2:
                logger_level = logging.INFO
            elif log_level_int == 3:
                logger_level = logging.WARNING
            elif log_level_int == 4:
                logger_level = logging.ERROR
            elif log_level_int == 5:
                logger_level = logging.CRITICAL
            return logger_level
    
        def __add_handlers(self):
            if self._is_add_stream_handler:
                for h in self.logger.handlers:
                    if isinstance(h, logging.StreamHandler):
                        break
                else:
                    self.__add_stream_handler()
            if all([self._log_path, self._log_filename]):
                for h in self.logger.handlers:
                    if os.name == 'nt':
                        if isinstance(h, RotatingFileHandler):
                            break
                    if os.name == 'posix':
                        if isinstance(h, (RotatingFileHandler, ConcurrentRotatingFileHandler)):
                            break
        
                else:
                    self.__add_file_handler()
    
        def __add_stream_handler(self):
            """
            日志显示到控制台
            """
            stream_handler = logging.StreamHandler()
            stream_handler.setLevel(self._logger_level)
            stream_handler.setFormatter(self._formatter)
            self.logger.addHandler(stream_handler)
    
        def __add_file_handler(self):
            """
            日志写入日志文件
            """
            if not os.path.exists(self._log_path):
                os.makedirs(self._log_path)
            log_file = os.path.join(self._log_path, self._log_filename)
            os_name = os.name
            rotate_file_handler = None
            if os_name == 'nt':
                # windows下用这个,非进程安全
                rotate_file_handler = RotatingFileHandler(log_file, mode="a", maxBytes=self._log_file_size * 1024 * 1024, backupCount=10,
                                                          encoding="utf-8")
            if os_name == 'posix':
                # linux下可以使用ConcurrentRotatingFileHandler,进程安全的日志方式
                rotate_file_handler = ConcurrentRotatingFileHandler(log_file, mode="a", maxBytes=self._log_file_size * 1024 * 1024,
                                                                    backupCount=10, encoding="utf-8")
            rotate_file_handler.setLevel(self._logger_level)
            rotate_file_handler.setFormatter(self._formatter)
            self.logger.addHandler(rotate_file_handler)
    
    
    import time
    
    
    class Test(unittest.TestCase):
        # @unittest.skip
        def test_repeat_add_handlers_(self):
            """测试重复添加handlers"""
            test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
            test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
            test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
            test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
            print('下面这一句不会重复打印四次和写入日志四次')
            time.sleep(1)
            test_log.debug('这一句不会重复打印四次和写入日志四次')
    
    
        def test_get_logger_without_hanlders(self):
            """测试没有handlers的日志"""
            log = LogManager('test2').get_logger_without_handlers()
            print('下面这一句不会被打印')
            time.sleep(1)
            log.info('这一句不会被打印')
    
    
        def test_add_handlers(self):
            """这样可以在具体的地方任意写debug和info级别日志,只需要在总闸处规定级别就能过滤,很方便"""
            log0 = LogManager('test3').get_logger_and_add_handlers(2)
            log1 = LogManager('test3').get_logger_without_handlers()
            print('下面这一句是info级别,可以被打印出来')
            time.sleep(1)
            log1.info('这一句是info级别,可以被打印出来')
            print('下面这一句是debug级别,不能被打印出来')
            time.sleep(1)
            log1.debug('这一句是debug级别,不能被打印出来')
    
        def test_only_write_log_to_file(self):
            """只写入日志文件"""
            log5 = LogManager('test5').get_logger_and_add_handlers(is_add_stream_handler=False, log_path='../logs', log_filename='test5.log')
            print('下面这句话只写入文件')
            log5.debug('这句话只写入文件')
    
    
    if __name__ == "__main__":
        unittest.main()

    3、在__add_handlers中添加判断,如果该日志已经有该类型的handlers,就不再添加。

    无论添加多少次日志handler,都不会重复打印。这在把日志实例作为模块里面的全局变量,项目中多个地方反复import这个模块,造成日志重复打印,可以很好解决掉。

  • 相关阅读:
    POJO,简单的Java对象
    Oracle 11g R2 for Win7旗舰版(64位)- 安装
    Eclipse安装与配置
    Tomcat 安装、配置与部署
    图形数据库 Neo4j 开发实战【转载】
    Java 语言中 Enum 类型的使用介绍【转载】
    IBM Java 7 新特性和在 WAS 8.5 中的配置【转载】
    为Eclipse指定JVM
    网页页面的宽度:950 还是 960?
    Zabbix + Grafana
  • 原文地址:https://www.cnblogs.com/ydf0509/p/9087913.html
Copyright © 2011-2022 走看看