python日志库logging

logging的组成

logging日志功能主要涉及到以下几个对象类型:

Logger:日志类,提供了应用程序可以直接调用的接口方法。程序代码通过调用Logger对象的相关方法来打印日志。Logger类包含level(日志级别)、handlers(处理器)、filters(过滤器)等属性,可通过设置其属性来定制Logger对象,指定日志的输出级别、输出格式、输出位置等信息。

Handler :处理器,
负责将(日志记录器产生的)日志记录输出到合适位置。

Filter :过滤器, 提供了更好的粒度控制,它可以决定输出哪些日志记录。

Formatter:格式器, 指明了最终输出的日志记录的格式。

LogRecord :日志记录器,将日志传到相应的处理器处理。

logging的使用

使用logging打印日志的具体代码如下:

import logging

# 1.创建logger对象
logger = logging.getLogger(__name__)

# 2.设置logger对象的相关参数
# 2.1 设置日志处理级别
logger.setLevel(logging.DEBUG)

# 2.2 指定logger对象的处理器
# 2.2.1 创建Handler对象
# 创建一个输出到控制台的Handler
console = logging.StreamHandler()
console.setLevel(logging.ERROR)   # 设置handler的日志处理级别

# 创建一个输出到log文件的Handler.
logfile = logging.FileHandler('test_log.log')
logfile.setLevel(logging.DEBUG)  # 设置handler的日志处理级别
file_format = logging.Formatter('%(asctime)s – %(name)s – %(levelname)s – %(message)s')
logfile.setFormatter(file_format)  # 设置日志输出格式

# 2.2.2 将handler绑定到logger
logger.addHandler(logfile)
logger.addHandler(console)

# 3.调用logger对象打印不同级别的日志
logger.error('this is error ')
logger.info('this is info ')
logger.debug('this is debug ')

创建logger对象

通过调用logging.getLogger方法创建logger对象,该方法需传入一个参数,用来指定logger对象的名称。建议传入__name__,以模块名为日志对象的名称,使得能直观的根据名称知道日志记录事件发生的位置。

设置logger对象的相关参数

1.设置日志级别

通过logger对象的setLevel方法可设置日志处理级别,低于设置级别的日志不会处理。

若没有给logger设置日志级别,则使用父logger的日志级别(若父logger也没有设置,继续找上一层logger的配置,直到root logger,root logger的默认日志处理级别为WARNING)。

2.指定logger对象的处理器

logging提供了多种类型的处理器,来处理日志并输出到指定位置。
StreamHandler:将日志记录输出发送到流,例如sys.stdout,sys.stderr或任何支持write()和flush()方法的对象。
FileHandler:将日志记录输出发送到磁盘文件。它继承了StreamHandler的输出功能。

Logger对象支持添加多个处理器。日志记录会被绑定的多个处理器处理,输出到不同位置。如上述代码示例中,日志会同时输出到控制台和文件。可根据实际情况选择需要的处理器类型及处理器的个数。

处理器支持日志级别,日志格式属性的设置。通过给不同的处理器设置不同的配置,来实现针对不同位置输出不同级别和格式的日志。当处理器未进行日志级别或格式的配置时,默认使用logger对象的配置。

logger对象和处理器都可设置日志处理级别,处理日志时,会先使用logger的日志处理级别配置去判断日志是否要处理,若需要处理,将日志记录传给handle对象,handle对象再根据其配置的日志处理级别判断是否对日志做处理。
所以如果handle对象的日志处理级别设置为DEBUG,低于logger对象的日志处理级别,DEBUG的日志不会输出。

logging的处理逻辑

image

logger间的继承关系

logger是通过名字来决定继承关系。比如你在设置logger时,将一个logger命名为logging.getLogger('log'),另一个命名为logging.getLogger('log.test'),那么前者就是后者的父logger。并且存在根logger(RootLogger实例),logging.getLogger('log')这个对象的父logger就是根logger。

logger对象有一个propagate属性,当该属性为true时(默认值就是ture),会在当前logger对象的所有handles处理完日志记录之后,将日志记录传给父logger的handles处理,父logger若propagate也是ture,也会传递给其父logger处理器处理,循环往复,最终到root logger。

因而会出现以下情况,如果当前logger设置了输出到控制台的handle,其父辈logger也设置了输出到控制台的handle,这就会导致同一条日志记录会在控制台输出多次。

import logging.handlers

formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")

# 定义父logger
father_logger = logging.getLogger("log")
father_logger.setLevel(logging.WARNING)
handler = logging.StreamHandler()
# handler.setLevel(logging.WARNING)
handler.setFormatter(formatter)
father_logger.addHandler(handler)

# 定义子logger
logger = logging.getLogger("log.test")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)

# 使用子logger 打印日志
logger.debug('This is a customer debug message')
logger.info('This is a customer info message')
logger.warning('This is a customer warning message')

上述代码中,每条日志记录会由logger对象的handle以及父logger对象的handle依次处理,控制台输出内容如下:

2019-11-22 15:09:40,949 log.test INFO This is a customer info message
2019-11-22 15:09:40,949 log.test INFO This is a customer info message
2019-11-22 15:09:40,949 log.test WARNING This is a customer warning message
2019-11-22 15:09:40,949 log.test WARNING This is a customer warning message

上述代码父辈logger设置为Warning级别,依然打印了两条INFO级别的记录。日志记录传递给父logger的handle对象做处理,会依据父logger的handle对象的级别设置判断是否输出日志,而与父logger的设置无关。

若想避免日志重复输出的情况,有两种方式:
(1)确保同一类型handler只对父辈logger或子logger其中一个对象进行设置。
(2)将propagate设置为False,不受传递记录给父辈logger。

可以通过logger.parent.handlers查看父logger的handle

root logger

logging模块提供了一种简洁的打印日志的方法,供简单的程序使用。

import logging
logging.warning('this is warning')
logging.error('this is error')

上述打印日志的方法实际上就是调用的root logger的方法。root logger为RootLogger实例,日志处理级别为WARNING。

# /logging/__init__.py

root = RootLogger(WARNING)
...
def warning(msg, *args, **kwargs):
    """
    Log a message with severity 'WARNING' on the root logger. If the logger has
    no handlers, call basicConfig() to add a console handler with a pre-defined
    format.
    """
    if len(root.handlers) == 0:
        basicConfig()
    root.warning(msg, *args, **kwargs)

logging.warning方法先判断root对象是否添加了handles对象,如果没有,调用basicConfig()添加一个控制台输出的handle对象给root,再调用root的warning方法。

当需要自定义配置root logger时,同样可以调用basicConfig()方法进行配置。函数的参数如下:

  • filename:使用提供的filename为RootLogger创建一个FileHandler,而不是StreamHandler;
  • filemode:如果filename已经指定,则指定这个filename的打开方式,默认为'a';
  • format:为handler设置日志格式;
  • datefmt:指定日期/时间格式;
  • level:设置 root logger的日志处理级别;
  • stream:用指定的steam去初始化StreamHandler,注意这个参数与filename参数不相容,如果filename,stream同时指定,那么stream参数会被忽略。
    使用方法如下:
import logging
logging.basicConfig(
    filename='log_root',
    filemode='a',
    format='%(asctime)s – %(name)s – %(levelname)s – %(message)s',
    datefmt='%m/%d/%Y %I:%M:%S',
    level=logging.DEBUG)
logging.info('this is info in root logger')

Refrence

Python logging模块介绍
Python logging
Python日志库logging总结
Python root logger解密

猜你喜欢

转载自www.cnblogs.com/anew/p/11984648.html
今日推荐