2.logging进阶教程

2.1简介

        Logging库采用模块化方法并且提供了几类组件,它们分别是loggers, handlers, filters, and formatters。

  • Loggers组件暴露出应用代码能够直接调用的接口。

  • Handlers组件把日志记录(Logger组件创建的)发给合适的目标。

  • Filter组件提供一个更优越的方式来决定输出哪个日志记录。

  • Formatters组件指定最终输出的日志记录的格式。
            在一个LogRecord实例中,事件日志信息会在loggers,handlers,filters和formatters四个组件中进行传递。
            通过调用Logger类实例的方法来执行日志记录(后面统称为loggers)。每个实例都有一个名称,并且它们在概念上以.作为分隔符,排在命名空间层次结构之后。例如,一个名为‘scan’日志记录器是‘scan.text’,‘scan.html’和‘scan.pdf’记录器的父集。Logger的名称可以取你想要的,建议最好是能表明该记录器所记录的区域。
            当命名日志器时有一个惯例,就是去使用模块级别的日志器(根据模块命名)。每一个要记录日志的模块都可以按如下进行命名:

    logger = logging.getLogger(__name__)
    

        这意味着logger的名称是根据所追踪的包/模块进行命名的。这也能够在查询日志信息时很方便的知道是哪个包/模块所记录的日志信息。
        顶级层次的日志记录器被称为root logger。这些logger所使用的方法与root logger所调用的方法有着一样的名字,都叫debug()info()warning()error()critical()。这些功能和方法都具有相同的签名。root logger的名称在输出信息中是“root”。
        当然,如果想将日志信息输出到不同的位置,这也很方便。软件包中包含支持用于将日志消息写入文件,HTTP GET / POST位置,通过SMTP发送电子邮件,通用套接字,队列或特定于操作系统的日志记录机制(如syslog或Windows NT事件日志)。目标位置是由handler类来处理的。如果你想一些特殊的需求,内置的handler类也没有提供,那么这时需要你自己创建handler类来处理。
        默认情况下是不对任何日志消息设置目标位置的。你可以像基础教程中的那样,通过basicConfig()来设置目标位置(例如控制台或文件)。当你调用debug()info()warning()error()critical()这些方法时,它们会检测目标位置。如果没有设置,在委托给根记录器来进行实际的消息输出之前,它们会默认输出到控制台(sys.stderr)并且以默认的格式格式化输出信息。
        basicConfig()默认的输出信息格式如下:

severity:logger name:message

        你可以通过改变basicConfig()的format参数值来调整格式。有关如何构造格式字符串的所有选项,详情见 Formatter Objects

2.2日志记录流程

        在记录事件信息时,loggers与handlers的处理流程如下图所示:


logging_flow.png

2.2.1.Loggers

        Logger对象有3个作用。1.Logger对象对外提供了几个方法,这样应用程序能够在运行时记录消息。2.Logger对象根据消息的严重性(默认的过滤工具)或过滤对象来确定要处理的日志对象。3.Logger对象将相关的日志信息传递给所有感兴趣的日志处理器。
        Logger对象使用的最广泛的方法分为两类:配置和发送信息。
        以下是最常见的用于配置的方法:

  • Logger.setLevel()指定将要处理的最低严重性的日志消息,注意debug是严重级别最低的而critical是严重级别最高的。举个例子,如果严重级别设置为INFO,logger对象只会处理INFO, WARNING, ERRORCRITICAL消息并且会忽略DEBUG消息。
  • Logger.addHandler()Logger.removeHandler()从logger对象中添加和移除处理对象。
  • Logger.addFilter()Logger.removeFilter()从logger对象中添加和移除过滤对象。
            你不需要每次创建一个Logger对象都调用这些方法。
            一旦logger对象配置完成,以下方法就能创建出日志消息:
  • Logger.debug(), Logger.info(), Logger.warning(), Logger.error()Logger.critical()所有这些方法都将创建日志信息以及对应其名字的严重等级。这信息实际上是一个格式化的字符串,这字符串包含了标准的字符串替换语法如%s, %d, %f等等。这剩下的参数是一个对象列表用于替换日志信息中的占位符。关于参数**kwargs,日志方法只关心exc_info关键字的值,根据这个关键字来决定是否记录异常信息。
  • Logger.exception()创建一个类似于Logger.error()方法的日志消息。这两者之间的区别是Logger.exception()会有一个堆栈信息。这个通常是由一个异常处理器进行调用。
  • Logger.log()将日志级别作为一个显示参数。这个方法相比于上面的日志方法要相对冗余,但是可以自定义设置日志级别。
            getLogger()方法返回一个logger类实例的引用。通过name参数来指定该logger实例的名称,如果没有指定则命名为root。名字是以句点分隔的层次结构。当多次调用getLogger()方法指定的名称一样时,就会返回同一个对象的引用。在分层列表中较低的logger是列表中较高的logger的子项,举个例子,有一个名为foo的logger,那么名为foo.barfoo.bar.bazfoo.bam的logger都是foologger的子类。
            logger类有一个等效级别的概念。如果logger没有显示设置级别,那么logger会沿用它父级的有效级别。如果父级没找到,会在父级的父级中找,以此类推,直到找到一个有效的级别。根logger的有效级别为WARNING。当执行一个事件时,有效级别就是用于决定需不需要把事件传递给logger的处理器。
            子logger会将消息传播给父级相关的处理器。由于这个特性,你不必为一个应用中的所有logger进行定义和配置。只要你为顶级的logger配置处理程序并根据需要创建子logger就足够了。

2.2.2.Handlers

        Handler对象的作用是将合适的日志信息(基于日志信息的优先级)转发到handler指定位置。Logger对象能够通过addHandler()方法来添加0个或多个handler对象。有这样一个示例场景,一个应用,需要将所有的日志信息保存到日志文件中,error级别的日志或更高级别的输出到控制台,critical级别的日志需要使用邮件提醒。这个场景需要三个不同的处理器,每个处理器都负责将不同级别的日志信息发送到指定的位置。
        标准库中包含了许多处理器的类型(详见Useful Handlers)。在这个教程中主要使用StreamHandlerFileHandler两个处理器。
        handler类提供了一些方法供开发者使用。如果开发者准备使用内置的处理器对象,那么以下就是这些配置方法:

  • setLevel()用于指定处理器处理的最低级别。logger对象中也有一个setLevel()方法,logger中的是指定哪些信息转发给handle,而这里的指定哪些信息发送到指定位置。
  • setFormatter()选择一个formatter对象供handler使用。
  • addFilter()removeFilter()分别是配置filter和移除filter对象。

        应用程序代码不应直接实例化和使用Handler类。相反,Handler类是一个基类,它定义了所有处理程序应具有的接口,并建立了子类可以使用的一些默认行为。

2.2.3.Formatters

        Formatter对象将会配置日志消息的最终顺序,结构和内容。与logging.Handler基类不同,应用程序代码可以直接实例化formatter类,当然你有一些特殊的要求或行为,你可以编写formatter类的子类。formatter类的构造函数需要三个可选的参数——信息格式字符串,日期格式字符串和样式字符串。
                logging.Formatter.init(fmt=None, datefmt=None, style='%')
        如果没有设置信息格式字符串,那么会使用默认的信息格式。如果没有设置日期格式字符串,默认的日期格式如下:

%Y-%m-%d %H:%M:%S

        最后会加上毫秒数。style参数是%, ‘{’或‘$’中的一个。如果没有指定其中的一个,那么默认将会使用‘%’。如果style参数的值为‘%’,消息格式字符串就会使用%(<dictionary key>)s样式的字符串进行替换。这些可能的键在LogRecord attributes罗列出来了。如果style的值为‘{’,那么消息字符串的格式与str.format()保持一致。如果style的值为‘$’,那么消息格式字符串应该与string.Template.substitute()的格式保持一致。
        接下来的信息格式字符串的含义是以可读的时间格式,信息的严重级别和信息内容的顺序进行日志记录:

'%(asctime)s - %(levelname)s - %(message)s'

2.2.4.Configuring Logging

程序配置logging有以下3种方式:

  • 明确使用python代码创建loggers对象, handlers对象,和formatters对象并调用上面列出的配置方法进行配置。
  • 创建一个日志配置文件并使用fileConfig()方法来读取它。
  • 创建一个含有配置信息的字典,再将该字典传递给dictConfig()方法。
            有关最后两个选项的参考文档,详见Configuration functions。接下来的例子是使用python代码简单配置一个logger,一个控制台处理器和一个简单的格式化类:
import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')

        执行代码,显示如下:

$ python3 simple.py
2018-08-29 10:29:47,835 - simple_example - DEBUG - debug message
2018-08-29 10:29:47,835 - simple_example - INFO - info message
2018-08-29 10:29:47,836 - simple_example - WARNING - warning message
2018-08-29 10:29:47,836 - simple_example - ERROR - error message
2018-08-29 10:29:47,836 - simple_example - CRITICAL - critical message

        以下的例子使用配置文件进行配置:

import logging
import logging.config

logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

        以下是logging.conf文件:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

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

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

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

        输出如下:

$ python3 simple_file.py
2018-08-29 11:09:08,217 - simpleExample - DEBUG - debug message
2018-08-29 11:09:08,217 - simpleExample - INFO - info message
2018-08-29 11:09:08,218 - simpleExample - WARNING - warning message
2018-08-29 11:09:08,218 - simpleExample - ERROR - error message
2018-08-29 11:09:08,218 - simpleExample - CRITICAL - critical message

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • From:Python之日志处理(logging模块) - 云游道士 - 博客园 https://www.cnbl...
    vigny的先生阅读 7,566评论 3 5
  • 本文章是我大概三年前,在上家单位使用 Python 工作时结合官方文档做的整理。现在 Python 官方文档听说已...
    好吃的野菜阅读 217,814评论 14 232
  • 本文翻译自logging howto 基础教程 日志是跟踪软件运行时发生事件的一种手段。Python开发者在代码中...
    大蟒传奇阅读 9,695评论 0 17
  • 禾谷, 今天是马不停蹄的一天,上午英语课,下午提前去画画,然后再赶去参加信息杯比赛。比赛出来你的第一句话是抱着我说...
    草马束木阅读 1,337评论 0 0
  • 永久的黑暗, 那是无边的默然…… 猛然的一惊, 应是重生的必然。 从此无眠…… 我抱着生的希望坚持、坚持, 怀着死...
    丝润万物阅读 3,808评论 0 50

友情链接更多精彩内容