• python logging用法的简单总结


    官方文档:Logging facility for Python — Python 3 documentation

    基本用法

    logging默认配置了六种日志级别(括号为级别对应的数值),按优先级升序,依次为:

    • NOTSET(0)
    • DEBUG(10)
    • INFO(20)
    • WARNING(30)
    • ERROR(40)
    • CRITICAL(50)

    logging 执行时输出大于等于设置的日志级别的日志信息,如设置日志级别是 INFO,则 INFO、WARNING、ERROR、CRITICAL 级别的日志都会输出

    先来看一个简单的用法示例:

    import logging
    
    logging.basicConfig()  # 自动化配置
    logging.warning('This is a warning message')  # 默认的日志输出级别为Warning
    

    logging.basicConfig(**kwargs) 中常用的参数包括:

    • filename:指定使用指定的文件名而不是 StreamHandler 创建 FileHandler
    • filemode:如果指定 filename,则以此模式打开文件(‘r’、‘w’、‘a’)。默认为“a”
    • format:配置日志格式字符串,参考:logrecord-attributes
    • datefmt:配置时间格式字符串,支持time.strftime() 所接受的日期/时间格式
    • level:指定日志记录器级别,默认为WARNING
    • handlers:指定handlers,注意此参数与filename和filemode不兼容,同时配置时会报错

    basicConfig配置示例:

    logging.basicConfig(filename="test.log", 
                        filemode="w", 
                        format="%(asctime)s %(name)s:%(levelname)s:%(message)s", 
                        datefmt="%d-%M-%Y %H:%M:%S", 
                        level=logging.DEBUG)
    

    进阶配置

    我们先了解一下logging中的主要模块:

    • Loggers:expose the interface that application code directly uses.
    • Handlers:send the log records (created by loggers) to the appropriate destination.
    • Filters:provide a finer grained facility for determining which log records to output.
    • Formatters:specify the layout of log records in the final output.

    程序中发送的日志信息将被包装成LogRecord对象传入logging的各个组件中:

    Loggers

    Logger objects have a threefold job. First, they expose several methods to application code so that applications can log messages at runtime. Second, logger objects determine which log messages to act upon based upon severity (the default filtering facility) or filter objects. Third, logger objects pass along relevant log messages to all interested log handlers.

    Logger与handler之间的关系类似于邮件和邮箱,一封邮件可以在需要的时候抄送至多个收件箱。Logger常用的配置方法包括:

    每个Logger都有一个name属性,它代表了这个logger在用户程序中所属的模块(与作用域的概念类似),不同模块下logger的name可以通过.来组织层级关系,比如hookhook.sparkhook.spark.attachment等。不同层级的logger间有类似于编程对象中“继承”的关系:父logger的各种配置项都会被子logger继承(包括handler,filter等)

    我们可以通过logging.getLogger()方法获得logger对象并配置name。由于logger遵从单例模式,因此多次调用getLogger()并配置相同的name时,该接口将返回同一个logger对象

    Handlers

    何时我们需要多个Handler?

    As an example scenario, an application may want to send all log messages to a log file, all log messages of error or higher to stdout, and all messages of critical to an email address.

    标准库中提供了一系列预定义的Handler,参考: Useful Handlers,它们常用的配置方法包括:

    Formatters

    formatter用来配置日志的各种格式,它包括三个参数:

    logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
    

    其中:

    • fmt指定了日志的消息格式,如:'%(asctime)s - %(levelname)s - %(message)s'
    • datefmt指定了日期的组织格式,如:%Y-%m-%d %H:%M:%S
    • style允许用户指定用什么类型的标识符来描述前两个参数的内容。比如,在以上两行例子中,我们用的都是%标识符。除此之外,我们还可以用{}$标识符,具体内容请参考:LogRecord attributes

    整体配置

    Logging可以通过三种方法配置:

    1. 在python代码中显式地声明loggers、handlers和formatters等组件并调用相关方法进行配置
    2. 创建一个logging配置文件,并调用fileConfig()进行配置
    3. 声明一个包含配置信息的dict,并将它传入 dictConfig() 进行配置

    后两种配置方式的具体细节可参阅: Configuration functions,接下来展示一个第一种配置方式的简单demo:

    import logging
    
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    
    # add formatter to ch
    ch.setFormatter(formatter)
    
    # create logger
    logger = logging.getLogger('simple_example')
    logger.setLevel(logging.DEBUG)
    
    # add ch to logger
    logger.addHandler(ch)
    
    # 'application' code
    logger.debug('debug message')
    

    以上代码先创建了一个formatter,然后把它添加至handler,接着把该handler关联至一个logger,之后我们就可以用这个logger记录信息了

    代码样例

    一个日志模块封装样例:

    import os
    import logging
    from logging.handlers import RotatingFileHandler
    
    class Log:
    
        _logger = None
        _log_dir = None
    
        @staticmethod
        def get():
            if Log._logger:
                return Log._logger
            else:
                return Log._build_logger()
    
        @staticmethod
        def set_dir(file_dir):
            if Log._log_dir:
                raise Exception('log directory has already been set. (Check if "get()" has been called before)')
            else:
                if not (os.path.exists(file_dir) and os.path.isdir(file_dir)):
                    os.mkdir(file_dir)
                Log._log_dir = file_dir
                
        @staticmethod
        def filter(name, level):
            level = str.upper(level)
            assert level in {'DEBUG', 'INFO', 'WARNING', 'ERROR'}
            logging.getLogger(name).setLevel(getattr(logging, level))
        
        @staticmethod
        def _build_logger():
            log_fmt = '[%(asctime)s | \"%(filename)s\" line %(lineno)s | %(levelname)s]  %(message)s'
            formatter = logging.Formatter(log_fmt, datefmt="%Y/%m/%d %H:%M:%S")
            
            if not Log._log_dir:
                os.makedirs('logs', exist_ok=True)
                Log._log_dir = 'logs'
    
            log_filepath = os.path.join(Log._log_dir, "rotating.log")
            log_file_handler = RotatingFileHandler(filename=log_filepath, maxBytes=500, backupCount=3)
            log_file_handler.setFormatter(formatter)    
    
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
    
            logging.basicConfig(level=logging.INFO, handlers=(log_file_handler, stream_handler))
            Log._logger = logging.getLogger()
            return Log._logger
    
    
    if __name__ == '__main__':
        import time
    
        # Log.set_dir('logs')  # Configure the log directory before use, if needed.
        log = Log.get()
        Log.set_dir('logs')
        for count in range(20):
            log.error(f"logger count: {count}")
            time.sleep(1)
    
    

    另一种写法,根据调用者的文件位置自动给logger命名:

    import os
    import logging
    import traceback
    from logging.handlers import RotatingFileHandler
    
    class Log:
    
        _root = None
        _logger = None
    
        @staticmethod
        def get():
            # Construct the name of the logger based on the file path
            code_file = traceback.extract_stack()[-2].filename  # Get the file path of the caller
            if Log._root not in os.path.abspath(code_file):
                raise Exception(f'The file calling the method is outside the home directory: "{code_file}"')
            relpath = os.path.relpath(code_file, Log._root).replace('.py', '').replace('/', '.')
            root_name = os.path.basename(Log._root)
            return logging.getLogger(f"{root_name}.{relpath}")
    
        @staticmethod
        def init(home):
            assert os.path.isdir(home), f'invalid home directory: "{home}"'
            Log._root = os.path.abspath(home)
            log_dir = os.path.join(Log._root, 'logs')
            if not os.path.isdir(log_dir):
                os.mkdir(log_dir)
            Log._configure_root_logger(log_dir)
    
        @staticmethod
        def filter(name, level):
            level = str.upper(level)
            assert level in {'DEBUG', 'INFO', 'WARNING', 'ERROR'}
            logging.getLogger(name).setLevel(getattr(logging, level))
        
        @staticmethod
        def _configure_root_logger(log_dir):
            log_fmt = '[%(asctime)s | %(name)s | %(levelname)s]  %(message)s'
            formatter = logging.Formatter(log_fmt, datefmt="%Y/%m/%d %H:%M:%S")
    
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
    
            log_filepath = os.path.join(log_dir, "rotating.log")
            log_file_handler = RotatingFileHandler(filename=log_filepath, maxBytes=1e5, backupCount=3)
            log_file_handler.setFormatter(formatter)    
    
            root_logger_name = os.path.basename(Log._root)
            root_logger = logging.getLogger(root_logger_name)
            root_logger.addHandler(log_file_handler)
            root_logger.addHandler(stream_handler)
            root_logger.setLevel(logging.DEBUG)  # default level
    
    
    if __name__ == '__main__':
        import os, time
    
        log_dir = os.path.dirname(__file__)
        Log.init(log_dir)  # project root directory
        log = Log.get()
        for count in range(20):
            log.info(f"logger count: {count}")
            time.sleep(1)
    
    
  • 相关阅读:
    Python面向对象编程
    Python模块
    Python函数式编程(把函数作为参数传入)
    Python函数高级特性
    Python函数基础
    连续数字或英文字符文本强制换行
    flex布局文本过长不显示省略号
    在div中放一个相同大小的svg,实际显示的位置svg偏下
    设置git push默认branch
    c# using
  • 原文地址:https://www.cnblogs.com/lokvahkoor/p/15838662.html
Copyright © 2020-2023  润新知