V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
1024dada
V2EX  ›  问与答

Python 常用内建模块-logging

  •  
  •   1024dada · 2018-12-21 13:53:32 +08:00 · 1169 次点击
    这是一个创建于 2167 天前的主题,其中的信息可能已经有所发展或是发生改变。

    简单使用

    #!/usr/local/bin/python
    # -*- coding:utf-8 -*-
    import logging
    

    logging.debug('debug message') logging.info('info message') logging.warn('warn message') logging.error('error message') logging.critical('critical message')

    输出:

    WARNING:root:warn message
    ERROR:root:error message
    CRITICAL:root:critical message

    默认情况下,logging 模块将日志打印到屏幕上(stdout),日志级别为 WARNING(即只有日志级别高于 WARNING 的日志信息才会输出),日志格式如下图所示:

    问题来了

    日志级别等级及设置是怎样的?
    怎样设置日志的输出方式?比如输出到日志文件中?

    简单配置

    日志级别

    <colgroup><col width="49.923547400611625%"><col width="50.076452599388375%"></colgroup>
    DEBUG详细信息,典型地调试问题时会感兴趣。
    INFO证明事情按预期工作。
    WARNING表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。
    ERROR由于更严重的问题,软件已不能执行一些功能了。
    CRITICAL严重错误,表明软件已不能继续运行了。

    简单配置

    #!/usr/local/bin/python
    # -- coding:utf-8 --
    import logging

    # 通过下面的方式进行简单配置输出方式与日志级别 logging.basicConfig(filename='logger.log', level=logging.INFO)

    logging.debug('debug message') logging.info('info message') logging.warn('warn message') logging.error('error message') logging.critical('critical message')

    输出:
    标准输出(屏幕)未显示任何信息,发现当前工作目录下生成了 logger.log ,内容如下:

    INFO:root:info message
    WARNING:root:warn message
    ERROR:root:error message
    CRITICAL:root:critical message

    因为通过 level=logging.INFO 设置日志级别为 INFO,所以所有的日志信息均输出出来了。

    问题又来了

    通过上述配置方法都可以配置那些信息?

    在解决以上问题之前,需要先了解几个比较重要的概念,LoggerHandlerFormatterFilter

    几个重要的概念

    • Logger 记录器,暴露了应用程序代码能直接使用的接口。
    • Handler 处理器,将(记录器产生的)日志记录发送至合适的目的地。
    • Filter 过滤器,提供了更好的粒度控制,它可以决定输出哪些日志记录。
    • Formatter 格式化器,指明了最终输出中日志记录的布局。

    Logger 记录器

    Logger 是一个树形层级结构,在使用接口 debug,info,warn,error,critical 之前必须创建 Logger 实例,即创建一个记录器,如果没有显式的进行创建,则默认创建一个 root logger,并应用默认的日志级别(WARN),处理器 Handler(StreamHandler,即将日志信息打印输出在标准输出上),和格式化器 Formatter(默认的格式即为第一个简单使用程序中输出的格式)。

    创建方法: logger = logging.getLogger(logger_name)

    创建 Logger 实例后,可以使用以下方法进行日志级别设置,增加处理器 Handler。

    • logger.setLevel(logging.ERROR) # 设置日志级别为 ERROR,即只有日志级别大于等于 ERROR 的日志才会输出
    • logger.addHandler(handler_name) # 为 Logger 实例增加一个处理器
    • logger.removeHandler(handler_name) # 为 Logger 实例删除一个处理器

    Handler 处理器

    Handler 处理器类型有很多种,比较常用的有三个,StreamHandlerFileHandlerNullHandler

    创建 StreamHandler 之后,可以通过使用以下方法设置日志级别,设置格式化器 Formatter,增加或删除过滤器 Filter。

    • ch.setLevel(logging.WARN) # 指定日志级别,低于 WARN 级别的日志将被忽略
    • ch.setFormatter(formatter_name) # 设置一个格式化器 formatter
    • ch.addFilter(filter_name) # 增加一个过滤器,可以增加多个
    • ch.removeFilter(filter_name) # 删除一个过滤器

    StreamHandler

    创建方法: sh = logging.StreamHandler(stream=None)

    FileHandler

    创建方法: fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)

    NullHandler

    NullHandler 类位于核心 logging 包,不做任何的格式化或者输出。本质上它是个“什么都不做”的 handler,由库开发者使用。

    Formatter 格式化器

    使用 Formatter 对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。

    创建方法: formatter = logging.Formatter(fmt=None, datefmt=None)

    其中,fmt 是消息的格式化字符串,datefmt 是日期字符串。如果不指明 fmt,将使用'%(message)s'。如果不指明 datefmt,将使用 ISO8601 日期格式。

    Filter 过滤器

    Handlers 和 Loggers 可以使用 Filters 来完成比级别更复杂的过滤。Filter 基类只允许特定 Logger 层次以下的事件。例如用‘ A.B ’初始化的 Filter 允许 Logger ‘ A.B ’, ‘ A.B.C ’, ‘ A.B.C.D ’, ‘ A.B.D ’等记录的事件,logger ‘ A.BB ’, ‘ B.A.B ’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。

    创建方法: filter = logging.Filter(name='')

    以下是相关概念总结:

    熟悉了这些概念之后,有另外一个比较重要的事情必须清楚,即Logger 是一个树形层级结构;
    Logger 可以包含一个或多个 Handler 和 Filter,即 Logger 与 Handler 或 Fitler 是一对多的关系;
    一个 Logger 实例可以新增多个 Handler,一个 Handler 可以新增多个格式化器或多个过滤器,而且日志级别将会继承。

    Logging 工作流程

    logging 模块使用过程

    1. 第一次导入 logging 模块或使用 reload 函数重新导入 logging 模块,logging 模块中的代码将被执行,这个过程中将产生 logging 日志系统的默认配置。
    2. 自定义配置(可选)。logging 标准模块支持三种配置方式: dictConfig,fileConfig,listen。其中,dictConfig 是通过一个字典进行配置 Logger,Handler,Filter,Formatter ; fileConfig 则是通过一个文件进行配置;而 listen 则监听一个网络端口,通过接收网络数据来进行配置。当然,除了以上集体化配置外,也可以直接调用 Logger,Handler 等对象中的方法在代码中来显式配置。
    3. 使用 logging 模块的全局作用域中的 getLogger 函数来得到一个 Logger 对象实例(其参数即是一个字符串,表示 Logger 对象实例的名字,即通过该名字来得到相应的 Logger 对象实例)。
    4. 使用 Logger 对象中的 debug,info,error,warn,critical 等方法记录日志信息。

    logging 模块处理流程

    1. 判断日志的等级是否大于 Logger 对象的等级,如果大于,则往下执行,否则,流程结束。
    2. 产生日志。第一步,判断是否有异常,如果有,则添加异常信息。第二步,处理日志记录方法(如 debug,info 等)中的占位符,即一般的字符串格式化处理。
    3. 使用注册到 Logger 对象中的 Filters 进行过滤。如果有多个过滤器,则依次过滤;只要有一个过滤器返回假,则过滤结束,且该日志信息将丢弃,不再处理,而处理流程也至此结束。否则,处理流程往下执行。
    4. 在当前 Logger 对象中查找 Handlers,如果找不到任何 Handler,则往上到该 Logger 对象的父 Logger 中查找;如果找到一个或多个 Handler,则依次用 Handler 来处理日志信息。但在每个 Handler 处理日志信息过程中,会首先判断日志信息的等级是否大于该 Handler 的等级,如果大于,则往下执行(由 Logger 对象进入 Handler 对象中),否则,处理流程结束。
    5. 执行 Handler 对象中的 filter 方法,该方法会依次执行注册到该 Handler 对象中的 Filter。如果有一个 Filter 判断该日志信息为假,则此后的所有 Filter 都不再执行,而直接将该日志信息丢弃,处理流程结束。
    6. 使用 Formatter 类格式化最终的输出结果。 注:Formatter 同上述第 2 步的字符串格式化不同,它会添加额外的信息,比如日志产生的时间,产生日志的源代码所在的源文件的路径等等。
    7. 真正地输出日志信息(到网络,文件,终端,邮件等)。至于输出到哪个目的地,由 Handler 的种类来决定。

    注:以上内容摘抄自第三条参考资料,内容略有改动,转载特此声明。

    再看日志配置

    配置方式

    • 显式创建记录器 Logger、处理器 Handler 和格式化器 Formatter,并进行相关设置;
    • 通过简单方式进行配置,使用 basicConfig()函数直接进行配置;
    • 通过配置文件进行配置,使用 fileConfig()函数读取配置文件;
    • 通过配置字典进行配置,使用 dictConfig()函数读取配置信息;
    • 通过网络进行配置,使用 listen()函数进行网络配置。

    basicConfig 关键字参数

    <colgroup><col width="49.923547400611625%"><col width="50.076452599388375%"></colgroup>
    filename创建一个 FileHandler,使用指定的文件名,而不是使用 StreamHandler。
    filemode如果指明了文件名,指明打开文件的模式(如果没有指明 filemode,默认为'a')。
    formathandler 使用指明的格式化字符串。
    datefmt使用指明的日期/时间格式。
    level指明根 logger 的级别。
    stream使用指明的流来初始化 StreamHandler。该参数与'filename'不兼容,如果两个都有,'stream'被忽略。

    有用的 format 格式

    <colgroup><col width="49.923547400611625%"><col width="50.076452599388375%"></colgroup>
    %(levelno)s打印日志级别的数值
    %(levelname)s打印日志级别名称
    %(pathname)s打印当前执行程序的路径
    %(filename)s打印当前执行程序名称
    %(funcName)s打印日志的当前函数
    %(lineno)d打印日志的当前行号
    %(asctime)s打印日志的时间
    %(thread)d打印线程 id
    %(threadName)s打印线程名称
    %(process)d打印进程 ID
    %(message)s打印日志信息

    有用的 datefmt 格式

    参考 time.strftime

    配置示例

    显式配置

    使用程序 logger.py 如下:

    # -- encoding:utf-8 --
    import logging

    # create logger logger_name = "example" logger = logging.getLogger(logger_name) logger.setLevel(logging.DEBUG)

    # create file handler log_path = "./log.log" fh = logging.FileHandler(log_path) fh.setLevel(logging.WARN)

    # create formatter fmt = "%(asctime)-15s %(levelname)s %(filename)s %(lineno)d %(process)d %(message)s" datefmt = "%a %d %b %Y %H:%M:%S" formatter = logging.Formatter(fmt, datefmt)

    # add handler and formatter to logger fh.setFormatter(formatter) logger.addHandler(fh)

    # print log info logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')

    文件配置

    配置文件 logging.conf 如下:

    keys=root,example01

    [logger_root] level=DEBUG handlers=hand01,hand02

    [logger_example01] handlers=hand01,hand02 qualname=example01 propagate=0

    [handlers] keys=hand01,hand02

    [handler_hand01] class=StreamHandler level=INFO formatter=form02 args=(sys.stderr,)

    [handler_hand02] class=FileHandler level=DEBUG formatter=form01 args=('log.log', 'a')

    [formatters] keys=form01,form02

    [formatter_form01] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

    使用程序 logger.py 如下:

    # -- encoding:utf-8 --
    import logging
    import logging.config

    logging.config.fileConfig("./logging.conf")

    # create logger logger_name = "example" logger = logging.getLogger(logger_name)

    logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')


    更多教程尽在每日答答官网: https://1024dada.com/?channel=v2ex

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5932 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 02:35 · PVG 10:35 · LAX 18:35 · JFK 21:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.