Python logging 模块详解

Python logging 模块详解

模块简介

This module defines functions and classes which implement a flexible event logging system for applications and libraries.
Python dictConfig 的配置,参考 Python 官方手册
https://docs.python.org/3/library/logging.config.html#logging-config-dictschema

Python logging 模块定义了为应用程序和库实现灵活的事件日志记录的函数和类。

程序开发过程中,很多程序都有记录日志的需求,并且日志包含的信息有正常的程序访问日志还可能有错误、警告等信息输出,Python 的 logging 模块提供了标准的日志接口,可以通过它存储各种格式的日志,日志记录提供了一组便利功能,用于简单的日志记录用法。

  • 使用 Python Logging 模块的主要好处是所有 Python 模块都可以参与日志记录
  • Logging 模块提供了大量具有灵活性的功能

日志记录函数以它们用来跟踪的事件的级别或严重性命名。下面描述了标准级别及其适用性(从高到低的顺序):

日志等级(level)描述
critical打印 critical 级别的日志,等级最高,打印一些致命的错误信息
error打印 error 以上级别的日志,打印一些错误信息
warning打印 warning 以上级别的日志,打印警告信息
info打印 info 以上级别的日志,正常输出信息,打印一些正常的操作
debug打印全部的日志 ( notset 等同于 debug ),最低级别,开发人员用来打印一些调试信息
notset打印全部的日志

日志级别等级排序critical > error > warning > info > debug(级别越高打印的日志越少,反之亦然)

Logging 模块提供了两种日志记录方式:

  • 使用 Logging 提供的模块函数

  • 使用 Logging 日志系统的四大组件记录

    • 记录器(Logger): 提供应用程序代码直接使用的接口。

      • 记录器 logger 实例包含每个默认日志级别的输入方法:

        logger.debug()

        logger.info()

        logger.warning()

        logger.error()

        logger.critical()

      • 其他两个日志记录调用:

        logger.log()

        ​ 手动发出具有特定日志级别的日志消息,随便你想记录什么都行。

        logger.exception()

        ​ 为了捕获某些异常,创建一个 ERROR 级别的日志(创建一个 ERROR 包装当前异常堆栈框架的级别日志记录消息)

    • 处理器(Handler): 将日志记录(由记录器创建)发送到适当的目的地。

    • 筛选器(Filter): 提供了更细粒度的功能,用于确定要输出的日志记录。

    • 格式器(Formatter):程序在最终输出日志记录的内容格式。

1. Logging 模块日志记录方式

1.1 Logging 定义的模块函数

1. 日志实例
import logging

# 打印日志级别
def test_logging():
    logging.debug('Python debug')
    logging.info('Python info')
    logging.warning('Python warning')
    logging.error('Python Error')
    logging.critical('Python critical')

test_logging()
2. 输出结果
WARNING:root:Python warning
ERROR:root:Python Error
CRITICAL:root:Python critical

'''
当指定一个日志级别之后,会记录大于或等于这个日志级别的日志信息,小于的将会被丢弃
默认情况下日志打印只显示大于等于 WARNING 级别的日志
'''

1.2 设置日志显示级别

通过 logging.basicConfig() 可以设置 root 的日志级别,和日志输出格式。

注意:Logging.basicConfig() 需要在开头就设置,在中间设置并无作用。

1. 日志实例
import logging

# 打印日志级别
def test():
    logging.basicConfig(level=logging.DEBUG)
    logging.debug('Python debug')
    logging.info('Python info')
    
test()
2. 输出结果
DEBUG:root:Python debug
INFO:root:Python info

1.3 文件记录日志信息

logging.basicConfig(
    # 控制台打印的日志级别
    level=logging.DEBUG,
    # 记录日志信息的文件
    filename='new.log',
    # a 是追加模式,默认如果不写的话就是追加模式;w 是写模式,每次都会重新写日志,覆盖之前的日志
    filemode='a',
    # 日志格式
    format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
	)

1.4 多模块记录日志信息

如果程序包含多个模块,则用以下实例来显示日志信息:

​ 实例中有两个模块,一个模块通过导入另一个模块的方式用日志显示另一个模块的信息

1. myapp.py 模块
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log',level=logging.DEBUG)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
2. mylib.py 模块
import logging

def do_something():
    logging.info('Doing something')
3. 输出结果
INFO:root:Started
INFO:root:Doing something
INFO:root:Finishe

1.5 显示日期及更改格式

1. 显示日志日期
import logging
# 显示消息时间
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')

logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('this event was logged.')

# 输出结果
2019-10-16 18:57:45,988 this event was logged.
2019-10-16 18:57:45,988 this event was logged.
2. 更改消息格式
import logging
# 更改显示消息的格式
logging.basicConfig(format='%(levelname)s:%(message)s',level=logging.DEBUG)
logging.debug('Python message format Debug')

# 输出结果
DEBUG:Python message format Debug

'''
显示结果只显示级别和具体信息,之前显示的 “根” 已经消失,重新定义的格式修改了默认输出方式。
'''

1.6 logger 层级结果与有效等级说明

关于 logger 的层级结构与有效等级的说明:

  • logger 的名称是一个以 ’ . ’ 分割的层级结构,每个 ’ . ’ 后面的 logger 都是 ’ . ’ 前面的 loggerchildren,例如,有一个名称为 foologger,其它名称分别为 foo.bar, foo.bar.bazfoo.bam 都是 foo 的后代。
  • logger 有一个"有效等级(effective level)"的概念。如果一个 logger 上没有被明确设置一个 level,那么该 logger 就是使用它 parentlevel;如果它的 parent 也没有明确设置 level 则继续向上查找 parentparent 的有效 level,依次类推,直到找到个一个明确设置了 level 的祖先为止。需要说明的是,root logger 总是会有一个明确的 level 设置(默认为 WARNING)。当决定是否去处理一个已发生的事件时,logger 的有效等级将会被用来决定是否将该事件传递给该 loggerhandlers 进行处理。
  • child loggers 在完成对日志消息的处理后,默认会将日志消息传递给与它们的祖先 loggers 相关的 handlers。因此,我们不必为一个应用程序中所使用的所有 loggers 定义和配置 handlers,只需要为一个顶层的 logger 配置 handlers,然后按照需要创建 child loggers 就可足够了。我们也可以通过将一个loggerpropagate 属性设置为 False 来关闭这种传递机制。

1.7 logger 日志流处理流程

在这里插入图片描述

1.8 总结

Logger是一个树形层级结构

Logger 可以包含一个或多个 HandlerFilter,即 LoggerHandlerFitler 是一对多的关系;

​ 一个 Logger 实例可以新增多个 Handler,一个 Handler 可以新增多个格式化器或多个过滤器,而且日志级别将会继承。

在这里插入图片描述

2. Logging 模块四大组件

2.1 日志器 - Logger

Logger 持有日志记录器的方法,日志记录器不直接实例化,而是通过模块级函数 logging.getlogger (name) 来实例化,使用相同的名称多次调用 getLogger() 总是会返回对相同 Logger 对象的引用。

  • 应用程序代码能直接调用日志接口
  • Logger 最常用的操作有两类:配置和发送日志消息
  • 初始化 logger = logging.getLogger(“endlesscode”),获取 logger 对象,getLogger() 方法后面最好加上所要日志记录的模块名字,配置文件和打印日志格式中的 %(name)s 对应的是这里的模块名字,如果不指定 name 则返回 root 对象
  • logger.setLevel(logging.DEBUG),Logging 级别 NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL,日志会记录设置级别以上的日志
  • 多次使用相同的 **name **调用 getLogger 方法返回同一个 logger 对象

Logger 是一个树形层级结构,在使用接口 debug,info,warn,error,critical 之前必须创建 Logger 实例:

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

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

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

2.2 处理器 - Handler

应用程序代码不应该直接实例化和使用 Handler 实例,因为 Handler 是一个基类,它只定义了所有 handlers 都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为;

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

Logger 是一个树形层级结构,Logger 可以包含一个或多个 HandlerFilter,即 LoggerHandlerFitler 是一对多的关系;

一个 Logger 实例可以新增多个 Handler,一个 Handler 可以新增多个格式化器或多个 Fitler 过滤器,而且日志级别将会继承。

1. StreamHandler

logging.StreamHandler 控制台输出

'''
StreamHandler 向类似与 sys.stdout/sys.stderr 的任何文件对象(file object)输出信息
构造函数:
	StreamHandler([strm])
	strm 参数是一个文件对象,默认是sys.stderr
'''

# 创建 StreamHandler 实例
sh = logging.StreamHandler(stream=None)
# 指定日志级别,低于WARN级别的日志将被忽略
ch.setLevel(logging.WARN) 
# 设置一个格式化器formatter
ch.setFormatter(formatter_name) 
# 增加一个过滤器,可以增加多个
ch.addFilter(filter_name) 
# 删除一个过滤器
ch.removeFilter(filter_name) 
2. FileHandler

logging.FileHandler 文件输出

'''
FileHandler 向一个文件输出日志信息
构造函数:
	FileHandler(filename[,mode])
	filename 是文件名,必须指定一个文件名。
	mode 是文件的打开方式,默认是’a',即添加到文件末尾。
'''

# 创建 FileHandler 实例
fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)
3. RotatingFileHandler

logging.handlers.RotatingFileHandler 按照大小自动分割日志文件,一旦达到指定的大小重新生成文件

'''
RotatingFileHandler 类似 FileHandler,有管理文件大小的功能,当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出。
比如日志文件是 chat.log,当 chat.log 达到指定的大小之后,RotatingFileHandler 自动把文件改名为 chat.log.1。不过,如果 chat.log.1 已经存在,会先把chat.log.1 重命名为 chat.log.2,最后重新创建 chat.log,继续输出日志信息。
构造函数
	RotatingFileHandler(filename[, mode[, maxBytes[, backupCount]]])
	filename 是文件名,必须指定一个文件名。
	mode 是文件的打开方式,默认是’a',即添加到文件末尾。
	maxBytes 用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
	backupCount 用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
'''
4. TimedRotatingFileHandler

logging.hanlders.TimedRotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按时间切割

5. HTTPHandler

logging.handlers.HTTPHandler 将日志消息以 GETPOST 的方式发送给一个 HTTP 服务器

6. SMTPHandler

logging.handlers.SMTPHandler 将日志消息发送给一个指定的 email 地址

7. NullHandler

logging.NullHandlerHandler 实例会忽略 error messages,通常被想使用 logginglibrary 开发者使用来避免 ‘No handlers could be found for logger XXX’ 信息的出现

2.3 过滤器 - Filter

Filter 可以被 HandlerLogger 用来做比 level 更细粒度的、更复杂的过滤功能。

Filter 是一个过滤器基类,它只允许某个 logger 层级下的日志事件通过过滤。该类定义如下:

class logging.Filter(name='')
    filter(record)

示例:

​ 一个 filter 实例化时传递的 name 参数值为’A.B’,那么该 filter 实例将只允许名称为类似如下规则的 loggers 产生的日志记录通过过滤:'A.B’,‘A.B,C’,‘A.B.C.D’,‘A.B.D’,而名称为’A.BB’, 'B.A.B’的 loggers 产生的日志则会被过滤掉。

​ 如果 name 的值为空字符串,则允许所有的日志事件通过过滤。

filter 方法用于具体控制传递的 record 记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。

说明:

  • 如果有需要,也可以在 filter(record) 方法内部改变该 record,比如添加、删除或修改一些属性。
  • 我们还可以通过 filter 做一些统计工作,比如可以计算下被一个特殊的 loggerhandler 所处理的 record 数量等。

2.4 格式器 - Formatter

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

创建方法

formatter = logging.Formatter(fmt=None, datefmt=None)

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

format 参数中可能用到的格式化信息:

parametersnotes
%(name)sLogger 的名字
%(levelno)s数字形式的日志级别
%(levelname)s文本形式的日志级别
%(pathname)s调用日志输出函数的模块的完整路径名 (可能没有)
%(filename)s调用日志输出函数的模块的文件名
%(module)s调用日志输出函数的模块名
%(funcName)s调用日志输出函数的函数名
%(lineno)d调用日志输出函数的语句所在的代码行
%(created)f当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896” (逗号后面的是毫秒)
%(thread)d线程 ID (可能没有)
%(threadName)s线程名 (可能没有)
%(process)d进程 ID (可能没有)
%(message)s用户输出的消息

2.5 组件之间的关联关系

  • 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,不同的处理器(handler)可以将日志输出到不同的位置;
  • 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
  • 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
  • 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。

简说:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日

志内容做过滤和格式化等处理操作。

  • Logger 可以包含一个或多个 HandlerFilter
  • LoggerHandlerFitler 是一对多的关系
  • 一个 Logger 实例可以新增多个 Handler,一个 Handler 可以新增多个格式化器或多个过滤器,而且日志级别将会继承。

3. Logging 日志工作流程

3.1 Logging 模块使用过程

  1. 第一次导入 logging 模块或使用 reload 函数重新导入 logging 模块,logging 模块中的代码将被执行,这个过程中将产生 logging 日志系统的默认配置。

  2. 配置(可选)

​ 1. 显示创建记录器 Logger、处理器 Handler 和格式化器 Formatter,并进行相关设置;

​ 2. 通过简单方式进行配置,使用 basicConfig() 函数直接进行配置;

​ 自定义:logging 标准模块支持三种配置方式: dictConfig,fileConfig,listen

​ 1. dictConfig() 是通过一个字典进行配置 Logger,Handler,Filter,Formatter;

​ 2. fileConfig() 则是通过一个文件进行配置;

​ 3. listen() 则监听一个网络端口,通过接收网络数据来进行配置。

​ 除了以上集体化配置外,也可以直接调用 Logger,Handler 等对象中的方法在代码中来显式配置。

  1. 使用 logging 模块的全局作用域中的 getLogger 函数来得到一个 Logger 对象实例

​ (其参数即是一个字符串,表示 Logger 对象实例的名字,即通过该名字来得到相应的 Logger 对象实例)。

  1. 使用 Logger 对象中的 debug,info,error,warn,critical 等方法记录日志信息。

3.2 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 的种类来决定。

  8. 最后,判断该日志器输出的日志消息是否需要传递给上一级 logger(之前提到过,日志器是有层级关系的)的处理器,如果propagate 属性值为1则表示日志消息将会被输出到处理器指定的位置,同时还会被传递给 parent 日志器的 handlers 进行处理直到当前日志器的propagate 属性为0停止,如果 propagate 值为 0 则表示不向 parent 日志器的 handlers 传递该消息,到此结束。

    可见,一条日志信息要想被最终输出需要依次经过以下几次过滤:

    • 日志器等级过滤;
    • 日志器的过滤器过滤;
    • 日志器的处理器等级过滤;
    • 日志器的处理器的过滤器过滤;

    说明: 关于上面第 9 个步骤,如果 propagate 值为 true,那么日志消息会直接传递交给上一级 loggerhandlers 进行处理,此时上一级 logger 的日志等级并不会对该日志消息进行等级过滤。

4. Logging 日志配置

4.1 python 代码方式

​ 使用 Python 代码实现日志配置

1. 日志实例
# 创建一个日志器logger并设置其日志级别为DEBUG
logger = logging.getLogger('simple_logger')
logger.setLevel(logging.DEBUG)

# 创建一个流处理器handler并设置其日志级别为DEBUG
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)

# 创建一个格式器formatter并将其添加到处理器handler
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

# 为日志器logger添加上面创建的处理器handler
logger.addHandler(handler)

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
2. 输出结果
2017-05-15 11:30:50,955 - simple_logger - DEBUG - debug message
2017-05-15 11:30:50,955 - simple_logger - INFO - info message
2017-05-15 11:30:50,955 - simple_logger - WARNING - warn message
2017-05-15 11:30:50,955 - simple_logger - ERROR - error message
2017-05-15 11:30:50,955 - simple_logger - CRITICAL - critical message

4.2 配置文件方式

​ 使用配置文件和 fileConfig() 函数实现日志配置

1. 配置文件(logging.conf)
[loggers]
keys=root,simpleExample

[handlers]
keys=fileHandler,consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
args=(sys.stdout,)
level=DEBUG
formatter=simpleFormatter

[handler_fileHandler]
class=FileHandler
args=('logging.log', 'a')
level=ERROR
formatter=simpleFormatter

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
2. 日志实例
# 读取日志配置文件内容
logging.config.fileConfig('logging.conf')

# 创建一个日志器logger
logger = logging.getLogger('simpleExample')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
3. 输出结果
2017-05-15 11:32:16,539 - simpleExample - DEBUG - debug message
2017-05-15 11:32:16,555 - simpleExample - INFO - info message
2017-05-15 11:32:16,555 - simpleExample - WARNING - warn message
2017-05-15 11:32:16,555 - simpleExample - ERROR - error message
2017-05-15 11:32:16,555 - simpleExample - CRITICAL - critical message
4. fileConfig() 函数说明

​ 该函数实际上是对 configparser 模块的封装,关于 configparser 模块的介绍请参考 http://www.cnblogs.com/yyds/p/6627208.html

# 函数定义(该函数定义在 loging.config 模块下):
logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True)

# 参数:
fname:   
	表示配置文件的文件名或文件对象
defaults:
	指定传给 ConfigParser 的默认值
disable_existing_loggers:
	这是一个布尔型值,默认值为 True(为了向后兼容)表示禁用已经存在的 logger,除非它们或者它们的祖先明确的出现在日志配置中;
    如果值为 False 则对已存在的 loggers 保持启动状态。
5. 配置文件格式说明

fileConfig() 函数是对 ConfigParser/configparser 模块的封装,也就是说 fileConfig() 函数是基于 ConfigParser/configparser 模块来理解日志配置文件的。换句话说,fileConfig() 函数所能理解的配置文件基础格式是与 ConfigParser/configparser 模块一致的,只是在此基础上对文件中包含的 sectionoption 做了一下规定和限制,比如:

  1. 配置文件中一定要包含 loggers、handlers、formatters 这些 section,它们通过 keys 这个 option 来指定该配置文件中已经定义好的 loggershandlersformatters,多个值之间用逗号分隔;另外 loggers 这个 section 中的 keys 一定要包含 root 这个值;

  2. loggers、handlers、formatters 中所指定的日志器、处理器和格式器都需要在下面以单独的 section 进行定义。seciton 的命名规则为**[logger_loggerName]、[formatter_formatterName]、[handler_handlerName]**

  3. 定义 loggersection 必须指定 levelhandlers 这两个 optionlevel 的可取值为 DEBUG、INFO、WARNING、ERROR、CRITICAL、NOTSET,其中 NOTSET 表示所有级别的日志消息都要记录,包括用户定义级别;handlers 的值是以逗号分隔的 handler 名字列表,这里出现的 handler 必须出现在 [handlers] 这个 section 中,并且相应的 handler 必须在配置文件中有对应的 section 定义;

  4. 对于非 root logger 来说,除了 levelhandlers 这两个 option 之外,还需要一些额外的 option,其中 qualname 是必须提供的 option,它表示在 logger 层级中的名字,在应用代码中通过这个名字得到 loggerpropagate 是可选项,其默认是为 1,表示消息将会传递给高层次 loggerhandler,通常我们需要指定其值为 0,这个可以看下下面的例子;另外,对于非 root loggerlevel 如果设置为 NOTSET,系统将会查找高层次的 logger 来决定此 logger 的有效 level

  5. 定义 handlersection 中必须指定 classargs 这两个 optionlevelformatter 为可选 optionclass 表示用于创建 handler 的类名,args 表示传递给 class 所指定的 handler 类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;levellogger 中的 level 一样,而 formatter 指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在 formatters 这个 section 中,且在配置文件中必须要有这个 formattersection 定义;如果不指定 formatter 则该 handler 将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息;

  6. 定义 formattersectioin 中的 option 都是可选的,其中包括 format 用于指定格式字符串,默认为消息字符串本身;datefmt 用于指定 asctime 的时间格式,默认为 ‘%Y-%m-%d %H:%M:%S’class 用于指定格式器类名,默认为 logging.Formatter

说明:

配置文件中的 class 指定类名时,该类名可以是相对于 logging 模块的相对值,如:FileHandler、handlers.TimeRotatingFileHandler;也可以是一个绝对路径值,通过普通的 import 机制来解析,如自定义的 handlermypackage.mymodule.MyHandler,但是 mypackage 需要在 Python 可用的导入路径中–sys.path

6. propagate 属性说明
实例1

logging.confsimpleExample 这个 handler 定义中的 propagate 属性值改为1,或者删除这个 option(默认值就是1

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=1

执行代码

# 读取日志配置文件内容
logging.config.fileConfig('logging.conf')

# 创建一个日志器logger
logger = logging.getLogger('simpleExample')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

发现除了在控制台有输出信息时候,在 logging.log 文件中也有内容输出

console 控制台输出

2022-09-28 16:58:57,841 - simpleExample - DEBUG - debug message
2022-09-28 16:58:57,841 - simpleExample - INFO - info message
2022-09-28 16:58:57,841 - simpleExample - WARNING - warn message
2022-09-28 16:58:57,841 - simpleExample - ERROR - error message
2022-09-28 16:58:57,841 - simpleExample - CRITICAL - critical message

logging.log 文件

2017-05-15 16:06:25,366 - simpleExample - ERROR - error message
2017-05-15 16:06:25,367 - simpleExample - CRITICAL - critical message

这说明 simpleExample 这个 logger 在处理完日志记录后,把日志记录传递给了上级的 root logger 再次做处理,所有才会有两个地方都有日志记录的输出。通常,我们都需要显示的指定 propagate 的值为 0,防止日志记录向上层 logger 传递。

实例2

试着用一个没有在配置文件中定义的 logger 名称来获取 logger

执行代码

# 读取日志配置文件内容
logging.config.fileConfig('logging.conf')

# 用一个没有在配置文件中定义的logger名称来创建一个日志器logger
logger = logging.getLogger('simpleExample_not_exist')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

logging.log 文件

2017-05-15 16:06:25,366 - simpleExample - ERROR - error message
2017-05-15 16:06:25,367 - simpleExample - CRITICAL - critical message

运行程序后,发现控制台没有任何输出,而 logging.log 文件中又多了两行输出,这是因为当一个日志器没有被设置任何处理器时,系统会去查找该日志器的上层日志器上所设置的日志处理器来处理日志记录。simpleExample_not_exist 在配置文件中没有被定义,因此 logging.getLogger(simpleExample_not_exist) 这行代码这是获取了一个 logger 实例,并没有给它设置任何处理器,但是它的上级日志器–root logger 在配置文件中有定义且设置了一个 FileHandler 处理器,simpleExample_not_exist 处理器最终通过这个 FileHandler 处理器将日志记录输出到 logging.log 文件中了。

4.3 字典配置方式

​ 使用字典配置信息和 dictConfig() 函数实现日志配置

Python 3.2 中引入的一种新的配置日志记录的方法–用字典来保存 logging 配置信息。这相对于上面所讲的基于配置文件来保存 logging 配置信息的方式来说,功能更加强大,也更加灵活,因为我们可把很多的数据转换成字典。比如,我们可以使用 JSON 格式的配置文件、YAML 格式的配置文件,然后将它们填充到一个配置字典中;或者,我们也可以用 Python 代码构建这个配置字典,或者通过 socket 接收 pickled 序列化后的配置信息。总之,你可以使用你的应用程序可以操作的任何方法来构建这个配置字典。

​ 日志实例使用 YAML 格式来完成与上面同样的日志配置。

1. 日志实例
# 需要安装 PyYAML 模块
pip install PyYAML

# python 代码
import logging
import logging.config
import yaml

with open('logging.yml', 'r') as f_conf:
    dict_conf = yaml.load(f_conf)
    
logging.config.dictConfig(dict_conf)

logger = logging.getLogger('simpleExample')
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
2. 配置文件(logging.yml)
version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
  console_err:
    class: logging.StreamHandler
    level: ERROR
    formatter: simple
    stream: ext://sys.stderr
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: yes
root:
  level: DEBUG
  handlers: [console_err]
3. 输出结果
2017-05-21 14:19:31,089 - simpleExample - DEBUG - debug message
2017-05-21 14:19:31,089 - simpleExample - INFO - info message
2017-05-21 14:19:31,089 - simpleExample - WARNING - warn message
2017-05-21 14:19:31,089 - simpleExample - ERROR - error message
2017-05-21 14:19:31,090 - simpleExample - CRITICAL - critical message
4. dictConfig() 函数说明

​ 该函数实际上是对 configparser 模块的封装,关于 configparser 模块的介绍请参考 https://www.cnblogs.com/yyds/p/6627208.html

# 函数定义 (该函数可以从一个字典对象中获取日志配置信息,config参数就是这个字典对象)
logging.config.dictConfig(config)
5. 配置字典说明

​ 无论是上面提到的配置文件,还是这里的配置字典,它们都要描述出日志配置所需要创建的各种对象以及这些对象之间的关联关系。

​ 比如,可以先创建一个名额为 “simple” 的格式器 formatter;然后创建一个名为 “console” 的处理器 handler,并指定该 handler 输出日志所使用的格式器为 “simple”;然后再创建一个日志器 logger,并指定它所使用的处理器为 “console”。

传递给 dictConfig() 函数的字典对象只能包含下面这些 keys,其中 version 是必须指定的 key,其它 key 都是可选项:

key 名称描述
version必选项,其值是一个整数值,表示配置格式的版本,当前唯一可用的值就是 1
formatters可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的格式器名称,value 为格式器的配置信息组成的 dict,如formatdatefmt
filters可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的过滤器名称,value 为过滤器的配置信息组成的 dict,如 name
handlers可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的处理器名称,value 为处理器的配置信息组成的 dcit,如 class、level、formatter 和 filters,其中 class 为必选项,其它为可选项;其他配置信息将会传递给 class 所指定的处理器类的构造函数,如下面的 handlers 定义示例中的 stream、filename、maxBytesbackupCount
loggers可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的日志器名称,value 为日志器的配置信息组成的 dcit,如level、handlers、filterspropagate
root可选项,这是 root logger 的配置信息,其值也是一个字典对象。除非在定义其它 logger 时明确指定 propagate 值为 no,否则 root logger 定义的 handlers 都会被作用到其它 logger
incremental可选项,默认值为 False。该选项的意义在于,如果这里定义的对象已经存在,那么这里对这些对象的定义是否应用到已存在的对象上。值为 False 表示,已存在的对象将会被重新定义。
disable_existing_loggers可选项,默认值为 True。该选项用于指定是否禁用已存在的日志器 loggers,如果 incremental 的值为 True 则该选项将会被忽略。
6. handlers 定义示例
handlers:
  console:
    class : logging.StreamHandler
    formatter: brief
    level   : INFO
    filters: [allow_foo]
    stream  : ext://sys.stdout
  file:
    class : logging.handlers.RotatingFileHandler
    formatter: precise
    filename: logconfig.log
    maxBytes: 1024
    backupCount: 3
7. 关于外部对象的访问

说明:

​ 上面所使用的对象并不限于 loggging 模块所提供的对象,我们可以实现自己的 formatterhandler 类。另外,这些类的参数也许需要包含 sys.stderr 这样的外部对象。如果配置字典对象是使用 Python 代码构造的,可以直接使用 sys.stdout、sys.stderr;但是当通过文本文件(如 JSON、YAML 格式的配置文件)提供配置时就会出现问题,因为在文本文件中,没有标准的方法来区分 sys.stderr 和字符串 ‘sys.stderr’。为了区分它们,配置系统会在字符串值中查找特定的前缀,例如 ‘ext://sys.stderr’‘ext://’ 会被移除,然后 import sys.stderr

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I believe I can fly~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值