【译】Python3.8官方Logging文档(完整版)

2023-05-16

注:文章很长,约一万字左右,可以先收藏慢慢看哟


01、基础部分

日志是用来的记录程序运行事件的工具。当程序员可以通过添加日志打印的代码来记录程序运行过程中发生的某些事件时。这些事件包含了诸如变量数据在内的描述信息,当然也包括开发人员认为重要的诸如日志级别等信息。

 

什么时候使用Loggin包

针对简单的场景,Loggin包提供了一系列方便的函数。其中包括debug()、info()、warning()、error()以及 critical()。下面的表格列举了什么函数适用于什么场景。

 

需求场景

适用工具

在命令行场景下,需要在控制台打印输出信息

print()

需要记录一些程序正常运行的事件信息(例如运行状态或者调试信息)

logging.info() (为了记录非常详细的调试信息也可以使用 logging.debug() )

担心程序运行时可能发生的某些告警

  • warnings.warn() 适用于可以通过调整代码主动避免问题的场景

  • logging.warning() 适用于不需要调整代码仅记录问题信息的场景

记录特定的错误信息

抛异常

记录那些不需要抛异常的错误信息(例如捕捉服务进程长时间运行期间的错误信息)

logging.error(), logging.exception() 或者 logging.critical() 都可以适用

 

日志函数的命名依据于约定俗成的事件级别。不同事件级别的适用范围可以参考下面的这个表格(按照级别的严重程度升序排列):

事件级别

适用范围

DEBUG

通常用在调试程序时输出详细信息

INFO

确认程序在正常运行

WARNING

在程序依旧能够正常运行的情况下:记录某个期望外的运行事件;记录一些达到临界值的运行信息(例如磁盘空间不足)

ERROR

因为某些严重的运行错误,影响程序的某些功能使用

CRITICAL

运行错误存在导致程序不能继续运行的场景

缺省的事件级别为WARNING,也就是说只有WARNING级别已经高于WARNING级别的事件可以被捕捉到。如果需要其他级别的信息,可以显式的设置日志级别。

针对捕捉到的运行事件有多种的处理措施,最简单的措施就是在控制台打印出来,当然也可以把这些事件信息记录到磁盘。

 

简单演示

下面是一个非常简单的用例:

import logginglogging.warning('Watch out!')  # 信息会被输出到控制台logging.info('I told you so')  # 这时不会打印任何信息

运行上述代码,执行结果如下:

WARNING:root:Watch out!

INFO级别的消息没有输出是因为缺省的日志级别是WARNING级别。观察输出信息,其中包含了日志级别以及调用日志函数时的入参信息(示例中为‘Watch out!’)。你可能会对中间的root有些疑惑,稍后会解释的。实际使用时,你可以根据需要灵活调整输出格式,对于输出格式稍后也会进一步解释。

 

把日志写入文件

这一部分是介绍比较实用的一种形式——是把日志记录在文件中。如果你打算运行下面的代码示例,记得新打开一个Python运行界面,而不是继续使用上面运行过的。

import logginglogging.basicConfig(filename='example.log',level=logging.DEBUG)logging.debug('This message should go to the log file')logging.info('So should this')logging.warning('And this, too')

执行完这段代码之后,打开日志文件就会发现多了这些日志内容:

DEBUG:root:This message should go to the log fileINFO:root:So should thisWARNING:root:And this, too

上面的实例也展示了可以在一开始通过配置改变日志级别。因为一开始就把日志级别设置为了DEBUG,所以对应的日志也被记录了下来。

 

如果你你希望通过以下方式在命令行配置日志级别:

--log=INFO

如果向--log传递了符合规范的变量值,可以使用:

getattr(logging, loglevel.upper())

来获取入参,进而传递给basicConfig()函数。或许你也想到了需要对入参进行校验,下面提供了一个校验入参的示例:

# assuming loglevel is bound to the string value obtained from the# command line argument. Convert to upper case to allow the user to# specify --log=DEBUG or --log=debugnumeric_level = getattr(logging, loglevel.upper(), None)if not isinstance(numeric_level, int):    raise ValueError('Invalid log level: %s' % loglevel)logging.basicConfig(level=numeric_level, ...)

 

basicConfig()函数的调用必须在debug()、info()以及其他日志输出函数之前。必须指出的是,baiscConfig()函数仅在第一次调用的时候生效,再次调用将不会产生任何作用。

 

如果你多次运行了上面的代码,你会发现日志文件里保存了多次运行的日志信息。可能你不需要把每次运行的日志信息都记录下来,而是仅保留最新的一次,那么你可以通过改变filemode 入参来实现,下面是一个示例:

 

logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)

这样话,虽然多次运行都是一样的日志,但是只有最新一次的运行日志被保留下来,之前的都会被刷掉。

 

在多个模块中使用Logging

如果你的程序包含多个模块,你可以参考下面的示例来组织你的代码:

# myapp.pyimport loggingimport mylibdef main():    logging.basicConfig(filename='myapp.log', level=logging.INFO)    logging.info('Started')    mylib.do_something()    logging.info('Finished')if __name__ == '__main__':    main()


 

# mylib.py
import loggingdef do_something():    logging.info('Doing something')

运行myapp.py后,你会发现myapp.log记录的日志信息如下:

INFO:root:StartedINFO:root:Doing somethingINFO:root:Finished

实际结果和你期望的一样。mylib.py的示例你可以灵活运用在你自己的多模块程序中。不过,需要注意的是,在这个简单的示例中,不同py文件的日志被穿插保存。《高级部分》(老张后面的推送会更新)部分会提供给你进一步的区分不同py文件日志信息的方法。

 

记录变量信息

通过格式化字符串的方式可以将想要保存的描述信息以及变量方便的保存下来。举个例子:

 

import logginglogging.warning('%s before you %s', 'Look', 'leap!')

运行结果如下:

 

WARNING:root:Look before you leap!

和你看到的一样,将变量格式化到描述信息的方式是使用的%这种旧的形式。这是为了向后兼容:logging包比新的格式化方式(比如str.format()、string.Template)诞生的要早。虽然logging也支持新的格式化方式,但是这里不做介绍。

 

自定义日志信息的格式

如果有需要,你可以自定义日志消息的格式:

import logginglogging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)logging.debug('This message should appear on the console')logging.info('So should this')logging.warning('And this, too')

运行结果如下:

DEBUG:This message should appear on the consoleINFO:So should thisWARNING:And this, too

可以看到之前一直在日志消息里面出现的“root”消失了。还有其他好多配置项,但是在简单场景下,你需要也许仅是levelname(级别),message(事件描述、变量信息),以及事件的发生时间。下面一章介绍时间配置。

 

展示时间信息

你可以通过增加‘%(asctime)s’来让你的程序输出时间信息:

import logginglogging.basicConfig(format='%(asctime)s %(message)s')logging.warning('is when this event was logged.')运行结果如下:
2010-12-12 11:41:42,612 is when this event was logged.

如上所示,默认的时间输出格式是ISO8601或者RFC 3339标准。如果你需要自定义时间格式,可以在调用basicConfig显示传入参数datefmt,示例如下:

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

运行结果如下:

12/12/2010 11:46:36 AM is when this event was logged.

datefmt的格式配置信息同time.strftime()一样。

 

总结

通过对《基础部分》的学习,你应该掌握了如何在程序里添加日志代码。logging还提供了高阶用法,为了能够掌握高阶用法,你应该继续阅读《高级部分》(老张这里顺道挖个坑)。

但是如果你仅仅是想跟上面介绍一样,简单的在程序里面使用日志,并且你有什么不明白的地方,可以把问题发在:

 

https://groups.google.com/forum/#!forum/comp.lang.python

你会很快收到回复的。

 

 

02、高级部分

Logging库包含了模块化的方法,提供包含loggers, handlers, filters以及formatters在内的若干组件:

  • loggers对外暴露了可以直接使用的接口。

  • handlers处理日志记录(由logger生产)的流向。

  • filters很便捷的决定日志记录是否能够被输出。

  • formatters包含了日志记录的输出格式。

事件信息会以LogRecord实例的形式在loggers、handlers、filters以及formatters之间传递。

 

Logging的入口为Logger类(以下统称loggers)的实例方法。每个实例都有一个命名,他们共同存在于一个由点符号分割的命名空间内。举个例子,一个被命名为‘scan’的logger实例是‘scan.text’实例的上层实例。你可以随意命名logger实例,并且可以在日志消息里面显示调用程序的层级。

 

一个比较好的做法是利用模块层级来组织logger实例的命名,模块里面的命名方式如下:

logger = logging.getLogger(__name__)

这样使得logger的命名可以正确反映包(以及模块)的层级,使得可以通过日志记录里面的logger命名直观的追溯到代码位置。

 

在层级根部实例化的logger被命名为root。同所有logger实例一样,root实例提供了debug(), info(), warning(), error()以及critical()函数。这些函数共享签名。root实例在打印日志信息时会携带‘root’字符串。

 

一条日志消息可以有多个流向。logging包提供的处理方式包括:写入文件、发送Get或者Post形式的HTTP报文、SMTP形式的电子邮件、通用的套接字、队列以及不同操作系统的日志处理机制(诸如系统日志、Windows NT事件日志)。日志消息的流向由handler类来处理。如果内置的handler类不能满足你的需求,你也可以自定义handler。

 

缺省状态下并不会设置任何日志流向。你可以通过《基础部分》里面提到的basicConfig()函数设置诸如控制台、文件在内的日志流向。当你调用debug()等日志方法时,这些方法会检查你是否指定了日志流向,如果你没有指定的话,这些方法会默认指定控制台(sys.stderr)为日志流向、使用默认的日志格式,然后才将日志消息传递到logger类的root实例,最终生成你看到的日志消息。

 

basicConfig()的缺省日志格式为:

severity:logger name:message

你也可以显式的通过format参数显式的的指定日志格式。关于日志格式的构造选项,请参考过于formatter类的文档说明。

 

Logging包的处理流程

下图是关于一条日志消息在loggers和handlers之间怎样被处理的流程图:

 

Loggers

logger对象有三部的工作。第一,它对外暴露了若干方法,使得外部程序可以在运行的时候记录日志信息。第二,logger对象可以根据日志级别来决定是否需要过滤掉一条日志消息。第三,logger对象会将日志消息传递给已关联的handlers。

 

logger对象有两类使用最广泛的方法:配置以及发送日志消息。

下面是最常见的配置方式:

  • Logger.setLevel()可以配置允许生效的最低级别。在内置的日志级别中,DEBUG级别是最低级的,CRITICAL是最高级别。举个例子,配置的级别是INFO,那么logger实例只会处理INFO、WARNING、ERROR以及CRITICAL级别的日志消息,而DEBUG级别的会被过滤掉。

  • Logger.addHandler()和Logger.removerHandler()为logger实例提供了增、删handler对象的途径。稍后会详细介绍handler对象。

  • Logger.addFilter()和Logger.removerFilter()为logger实例提供了增、删filter对象的途径。

     

你并不需要每次创建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()需要指定日志级别作为入参。相比上面提到的开箱即用的日志函数,它显得有些繁琐,但是可以适用于需要自定义日志级别的场景。

     

Handlers

handler对象负责根据日志级别分配日志消息的最终流向。Logger对象默认不包含handler对象,但是可用通过addHandler()方法添加。拿一个应用场景来举例:假设你的应用程序需要将所有日志消息保存在日志文件中;把ERROR级别及以上的日志打印在标准输出;所有的CIRITCAL级别的日志通过电子邮件发给你。整个场景需要三个handler实例,每个实例都会根据不同的日志级别采取不同的方式处理日志消息。

 

标准库只内置少量的handler类型;本文档主要拿StreamHandler和FileHandler来举例。

 

开发人员只需要关心Handler对象的少数几个方法。在内置的handler对象(非自定义的handler)里面,跟开发人员密切相关的配置方法如下所示:

  • setLevel()方法跟logger对象的方法一样,配置handler会处理的最低日志级别。为什么会有两个setLevel()方法呢?logger对象设置的日志级别决定了日志能够被传递到handler对象。而handler对象设置的日志级别决定了日志消息是否会被记录下来。

  • setFormaterr()可以为handler对象配置Formatter对象。

  • addFilter()和removerFilter()可以增删filter对象。

 

应用程序不应该直接实例化Handler对象。因为Handler对象是一个基类,它定义了所有Handler子类都应该继承或者复写的接口方法。

 

Formatters

formatter对象决定了一条的日志消息的顺序、结构以及内容。不同于logger.Handler是基类,应用程序需要自己实例化Formatter类。当然如果你有特殊需求,也可以实例化Formatter的子类。它接收三个参数:

  • 一个预格式化的消息字符串

  • 一个预格式化的时间字符串

  • 一个类型符号

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

如果没有显式的传入消息格式,会使用缺省设置。如果没有显式的传入时间格式,缺省的时间格式如下:

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

并且会在后面追加毫秒。类型符号可以选择‘%’、‘{'或者’$‘。缺省的类型符号为’%’。

  • 如果类型符号为‘%’,日志消息的格式化方式采用%(<dictionary key>)s的替换方式;可用的键值请单独翻阅LogRecord的说明。

  • 如果类型符号为‘{’,日志消息的格式化方式采用与str.formate()方法兼容的处理(也就是使用关键字)。

  • 如果类型符号为‘$’,日志消息的格式化方式需要与string.Template.substitute()方法保持一致。

3.2版本的改动说明:增加了style入参。

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

上面是一个带有时间的可读性高的预格式化方式,日志级别和日志内容被有序的添加在里面。

formatters提供了用户可配置的函数,方便日志生成时间转化为时间元组。默认的是使用time.localtime()。如果想要在formatter实例中自定义,可以通过给 converter属性赋值的形式改变默认行为,需要注意的是新赋的值需要是同time.localtime()或者time.gmtime()签名一致的函数。假设这么一个场景:你需要是所有的日志时间都展示为GMT时区,你可以将Formatter的 converter属性赋值为time.gmtime()的形式,改变所有formatter实例行为。

 

Logging配置

开发人员可以通过以下三种配置来配置logging:

  1. 在代码中使用前面提到的方式在代码中依次创建loggers、handlers以及formatters对象。

  2. 新建一个配置文件,并通过fileConfig()函数载入配置。

  3. 新建一个配置文件夹,并通过dictConfig()函数载入配置。

 

关于后两种配置方式更详细的说明,请自行查阅Configuration函数文档。下面是Python代码示例,它包含了一个简单的logger实例、一个控制台handler和一个简单的formatter:

import logging
​​​​​​​

# create loggerlogger = 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('warn message')
logger.error('error message')
logger.critical('critical message')

   

运行结果如下:

$ python simple_logging_module.py2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message2005-03-19 15:10:26,620 - simple_example - INFO - info message2005-03-19 15:10:26,695 - simple_example - WARNING - warn message2005-03-19 15:10:26,697 - simple_example - ERROR - error message2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

 

下面代码的效果同上面一样,也是新建logger实例、handler实例和formatter实例,唯一不同的地方是对象的命名。

import loggingimport logging.configlogging.config.fileConfig('logging.conf')# create loggerlogger = logging.getLogger('simpleExample')# 'application' codelogger.debug('debug message')logger.info('info message')logger.warning('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=DEBUGhandlers=consoleHandler[logger_simpleExample]level=DEBUGhandlers=consoleHandlerqualname=simpleExamplepropagate=0[handler_consoleHandler]class=StreamHandlerlevel=DEBUGformatter=simpleFormatterargs=(sys.stdout,)[formatter_simpleFormatter]format=%(asctime)s - %(name)s - %(levelname)s - %(message)sdatefmt=

执行之后的输出信息同不用配置文件的差不多:

$ python simple_logging_config.py2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message2005-03-19 15:38:55,979 - simpleExample - INFO - info message2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message2005-03-19 15:38:56,055 - simpleExample - ERROR - error message2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

 

能够很明显看到的是,配置文件的内容格式相对Python代码而言有一些简化的地方。像这样将配置和代码分离,能够帮助没有开发经验的用户更好的配置日志行为。


警告:fileConfig函数有一个特殊的入参——disable_existing_loggers。为了保持向后兼容,其默认值为True。这个参数导致的行为可能会让你困惑,它会使得在fileConfig()函数调用之前已存在的非root的logger实例失效,除非某个logger同配置文件中的配置同名。如果需要关于它的更多细节,可以自行查阅相关说明。当然你也可以根据实际需要显式的传入False。

 

需要注意的是配置文件提到的接口引用,要么必须是logging包内部的,要么是可通过import导入的绝对路径。举个例子,你可以使用WatchedFileHandler(logging包内部),你也可以用mypackage.mymodule.MyHandler(一个在mypackage包--mymodule模块定义的类,当然整个路径必须能够被import正确导入)。

 

从Python3.2开始,引入了一种新的日志配置方式--通过目录组织配置信息。它能够提供上面其他方式更强大的功能,推荐开发人员在新建项目时使用这种方式。因为文件夹的配置方式除了正常的配置之外,还可以根据不同的用途灵活的移动文件夹。举个例子,你可以使用JSON格式添加配置信息,如果你之前接触过YAML程序开发,你也使用YAML格式。当然,你也可以选择Python代码的配置方式、接收套接字的配置方式,或者其他你认为方便的方式。

 

下面是基于文件夹配置使用YAML格式的配置示例,效果跟之前的示例一样:

version: 1formatters:  simple:    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'handlers:  console:    class: logging.StreamHandler    level: DEBUG    formatter: simple    stream: ext://sys.stdoutloggers:  simpleExample:    level: DEBUG    handlers: [console]    propagate: noroot:  level: DEBUG  handlers: [console]

如果需要关于文件夹配置的更多资料,可以自行查阅Configration的说明。

如果没有提供配置信息会发生什么

如果没有提供配置信息,大概率会在打印日志事件的时候发现没有handlers实例可用。当然,实际会发生什么跟Python版本有关。

 

对于Python3.2之前的版本,最后会发生:

  • 如果logging.raiseExceptions选项为False(线上环境),日志会被丢弃。

  • 如果logging.raiseExceptions选项为True(开发环境),会在控制台输出‘No handlers could be found for logger X.Y.Z’ 

 

Python3.2及以后的版本,最后会发生:

  • 日志事件会通过loggin.lastResort对象中的兜底handler处理。

    这个内部的handler并没有被任何logger使用,它的效果跟StreamHandler一样,会将日志消息输出到sys.stderr(所以需要你谨慎的对待可能被改动的重定向)。

    它只会打印出来日志消息,并没有携带任何格式。

    这个handler的默认级别是DEBUG,所以基本上任何消息都会被打印出来。

如果你希望禁用3.2版本之后的默认行为,可以将logging.lastResort显式的的设置为None。


 

在库中使用logging

如果你会在自己开发的库里面使用logging,那么你需要仔细确认库是如何使用logging的,举个例子:关于loggers的名字。仔细确认如何配置logging是必须的。如果调用方没有使用logging,但是库内部使用了logging,WARNING级别及以上的日志消息将会输出到sys.stderr。看过了之前的介绍,你应该知道这是默认行为。

 

如果你期望在没有主动配置的情况不输出这些日志消息,你可以给你的库里面最高层的logger实例添加一个没有任何操作的handler。这样就可以避免日志被打印出来,原因就是对于库内部而言日志消息都会交给这个handler处理,但是这个handler并不会打印输出任何东西。一旦,调用程序主动配置添加了handler对象,你的库内部产生日志消息也会被处理,如果配置的日志级别适配,那么消息将会被打印输出。

 

在Python3.1版本之后,NullHandler被引入,但是它实际上并不会对日志消息做任何处理。如果你不希望你的库内部日志在配置缺失的情况下被输出到sys.stderr,你可以实例化一个NullHandler,并把它添加到顶层的logger实例。举个例子,如果一个名为foo的库实例化了诸如‘foo.x’、‘foo.x.y’的logger,你可以这样:

​​​​​​​

import logginglogging.getLogger('foo').addHandler(logging.NullHandler())

在一个组织提供了多个库的场景下,这样做使得实际当中logger的命名为orgname.foo而不是foo。

提示:对于库,除了NullHandler之外,不要再添加任何其他handler。这是因为你应该把控制权交给调用方。只有调用方结合自己的实际情况能够决定使用什么样的handler。如果你在库里面添加了handler,你可能会影响调用方的单元测试结果,并且输出一些他们不需要的东西。

 

 

03、其他说明

 

Logging的日志级别

下表列出了日志级别对应的数字。如果你需要自定义日志界别,你需要避开内置的这些值。一旦你定义的日志级别和内置的冲突,内置级别会被复写、抹掉。

 

日志级别

相应数值

CRITICAL

50

ERROR

40

WARNING

30

INFO

20

DEBUG

10

NOTSET

0

日志级别跟logger实例关联方式有两种:一是开发人员主动配置,二是通过配置文件加载。当logger实例的日志方法被调用的时候,logger会检查它的级别配置跟日志方法是否匹配。如果logger的级别比日志方法要高,实际上就不会产生任何日志消息。它从构造机制上避免了产生冗余的输出。

 

日志消息实际上是LogRecord类的实例化。当logger决定生成一条日志消息的时候,实际上是根据消息内容实例化了LogRecord。

 

日志消息随后会由Handler子类的实例来处理。handler实例会根据日志级别决定一条日志消息的最终流向(可能会有多个流向,诸如用户、编辑、管理员、开发人员)。handler会将LogRecord实例传递到合适的目的地。每个logger实例都可以有0个、1个或者多个handler(还记得addHandler()函数吗)。当一个logger实例产生一条日志消息之后,它的所有handler、它上层logger的所有handler都会被调用(除非这个logger的propagate值被置为False),分别处理这个日志消息。

 

logger实例和handler实例的日志级别可以不一致。它们分别都会根据日志级别做过滤。在handler最终处理日志消息的时候,emit()方法将被用来向传递消息。大多Handler子类都复写了emit()方法。

 

自定义日志级别

你可能会自定义日志级别,但其实那是没必要,因为这些日志级别就是实践中总结出来的。如果你确定需要自定义,请务必小心,对于库开发而言这样做甚至是有害的。原因是,如果不同的库均自定义了日志级别,但是它们之间又不一致(一个数值可能对应多个不同级别),对于调用方的开发人员简直就是灾难。

 

实用的handler

作为对Handler基类的补充,几个实用的handler子类如下:

  1. StreamHandler实例会将消息发送到数据流(类似于文件对象)。

  2. FileHandler实例会将消息写入磁盘文件。

  3. BaseRotatingHandler是滚动日志的基类,滚动日志会在指定的时机滚动日志文件。它不能直接被实例化,实际使用的是RotatingFileHandler或者TimedRotatingHandler。

  4. RotatingFileHandler实例会将消息写入磁盘文件,并在日志文件的占用空间达到最大值的时候滚动日志文件。

  5. TimedRotatingHandler实例会将消息写入磁盘文件,并在根据指定的时间间隔滚动日志文件。

  6. SockerHandler实例会通过TCP/IP套接字发送日志消息。在3.4版本之后,Unix套接字也开始被支持。

  7. DatagramHandler实例会通过UDP发送日志消息。在3.4版本之后,Unix套接字也开始被支持。

  8. SMTPHandler实例会将日志消息发送到指定的电子邮箱。

  9. SysLogHandler实例可以把日志消息发送到Unix系统日志守护程序(可能是一台远程机器)。

  10. NTEventLogHandler实例会把日志消息发送到Windows NT/2000/XP系统日志。

  11. MemoryHandler实例会把日志消息写入内存的缓冲区,这意味着可以通过指令清除。

  12. HTTPHandler实例通过GET或POST的方式把日志消息发到HTTP服务器。

  13. WatchedFileHandler实例可以监控日志文件。一旦文件发生改动,它会关闭并重新打开一个同名文件。该handler只可以在类Unix系统上使用,因为Windows系统底层不支持。

  14. QueueHandler实例将日志消息保存到一个队列,就像queue或者multiprocessing模块实现的那样。

  15. NullHandler实例对于错误日志没有任何响应。如果在功能库的开发中想要使用logging,但是又要在没有配置信息时避免显示类似‘No handlers could be found for logger XXX’ 这种消息,可以考虑使用NullHandler。

注:NullHandler在3.1版本引入

注:QueueHandler在3.2版本引入

 

其中NullHandler、StreamHandler和FileHandler在logging核心包中定义。其他的handler在子模块logging.handlers中。(还有一个子模块logging.config用于配置)

 

已经输出的日志消息在在Formatter类内部被格式化的。通过带有‘%’符号的预格式化的字符串以及一个字典来初始化Formatter对象。

 

如果想要批处理多条消息,可以考虑使用BufferingFormatter实例。为了方便格式化字符串(即批处理),它提供了针对头尾字符串格式的要求。

 

如果通过配置logger和handler的日志级别不能够满足你的需要的时候,logger或者handler都可以单独配置Filter实例(通过调用addFilter()函数)。在进一步处理日志消息之前,logger和handler都会调用已注册的filter来判断是否过滤该消息。如果其中任何一个filter实例返回了False,那么这条消息都会被丢弃。

 

基本的Filter用法是通过logger名来过滤。如果该配置生效,那么发送到指定logger,以及其子类传递上来的日志消息均会被丢弃。

 

logging的异常处理

logging具备可以消化自身异常的能力。换句话说,logging本身运行产生的诸如配置错误、网络问题之类的错误并不会导致调用程序结束运行。

 

除了SystemExit和KeyboardInterrupt之外,emit()方法产生的其他异常将会被丢给自身的handleError()方法处理。

handlerError()方法会检查名为raiseExceptions的配置项,如果设置了该配置项,handler会将错误信息输出到sys.stderr,如果没有配置该配置项,错误信息会被内部消化。

提示:raiseExceptions的缺省值是True。知道你不希望在开发时被这些错误信息牵扯精力,所以默认就是True。但是建议你在线上环境要将raiseExceptions设置为False。

 

在消息里面使用任何对象

在上面的介绍里,你可能会发现了所有的日志消息都是字符串。并不是说明只有字符串才能当作日志消息传递给logging,实际上,你可以使用任何对象。logging内部会调用这些对象的__str__()方法,将他们转成字符串。你甚至可以完全避免用到字符串,举个例子:SocketHandler在发送消息的时候用到的二进制表示。

 

优化

虽然只有在最后用到的时候才会格式化日志消息,但是针对日志方法的入参做的运算也是巨大的,你可能期望logging对于被丢弃的消息节省这些计算。你可以通过调用isEnabledFor()方法(该方法接受一个日志界别的入参)来查看logging的行为,如果它返回了True即表明这个级别的日志消息会被Logger实例处理。代码示例如下:​​​​​​​

if logger.isEnabledFor(logging.DEBUG):    logger.debug('Message with %s, %s', expensive_func1(),                                        expensive_func2())

这样就可以确定logger实例的不处理DEBUG级别的日志,expensive_func1()和expensive_func2() 就不会被执行到。

提示:某些情况下isEnabledFor()方法可能比你想象的更昂贵(举个例子,嵌套的logger中,只有最上层的logger日志级别是高于入参的)。在这种场景下(或者你只是想要避免嵌套调用一个方法),你可以把首次调用isEnabledFor()的结果缓存下来,后续只调用缓存的本地变量)。这样你就可以仅在日志配置动态改变时计算新的变量值(其实这种场景并不常见)。

 

还有一种优化场景就是只有某种情况下希望能尽可能详细的收集日志信息。下表是一些你可以避免的一些资源浪费:

你不希望收集的信息

如何避免

日志消息在哪产生的

logging._srcfile 设置为None. 这样可以避免sys.getframe(),某些环境中(如pypy)可以帮助你的程序运行的更快。(不过支持py3的pypy环境该方法会失效)

线程信息

将 logging.logThreads 设置为 0.

进程信息

将 logging.logProcesses 设置为 0.

 

需要提醒的是logging核心模块仅包含基本的一些handler类。剩下的handler并不直接可用,而是需要你主动导入logging.handlers和logging.config。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【译】Python3.8官方Logging文档(完整版) 的相关文章

  • Python文章归档

    下面是最新的Python文章目录导航 Python基础面向对象语句函数文件正则表达式Python GUI 程序Python模块Python机器学习Python爬虫与Python相关的 内容更新至2018年1月22日 Python基础 xff
  • 7个优秀的境外程序员博客

    如果你想成为程序员 xff0c 你需要沉浸在编程文化中 如果你仍然是一个学生 xff0c 这是更真实的 编程领域非常广泛 xff0c 吸收了太多的信息 xff0c 如果你远距离参与 xff0c 你永远不会出现在最前面 幸运的是 xff0c
  • k-means算法概述

    优点与缺点 K means算法思想 K MEANS算法是输入聚类个数k xff0c 以及包含 n个数据对象的数据库 xff0c 输出满足方差最小标准k个聚类的一种算法 k means 算法接受输入量 k xff1b 然后将n个数据对象划分为
  • MATLAB随机数生成器

    1 rand 生成 0 1 区间上均匀分布的随机数 基本语法 xff1a rand M N P 生成排列成M N P 多维向量的随机数 如果只写M xff0c 则生成M M矩阵 xff1b 如果参数为 M N 可以省略方括号 2 randn
  • Python弹球游戏(tkinter模块编写)

    python弹球游戏 xff0c 能够选择难度和分数控制 程序利用tkinter模块编写 xff08 说到这里吐槽一下tkinter模块 xff0c 虽然tkinter是python自带的模块 xff0c 但是编写手感真没有wxpython
  • SSH登录时提示Read from socket failed: Connection reset by peer.

    首先查看日志 tail f var log auth log 添加参数 v 获得更详细的连接信息 ssh user 64 computerB v 1 如果是 rsa and dsa keys 丢失产生的问题 可以通过下面的方式进行修复 ss
  • Python密码存储器

    xff08 不使用数据库 xff09 设计一个密码记录及查询小软件 xff0c 模拟记录自己在各个网站上所使用的账号及密码 xff0c 保存在文件中 要求自行设计存储方式 xff0c 并实现浏览 xff0c 查询 xff0c 增加 xff0
  • Python英文搜索引擎(模糊搜索)

    假设在C Record下面有若干个 txt文件 xff0c 均为纯英文文档 以这些文档为内容 xff0c 实现一个本地搜索引擎 xff0c 当用户给出某个输入时 xff0c 列出相关的搜索结果 可以自行决定改搜索引擎的功能强弱 xff0c
  • wxpython记录生词GUI程序

    不使用数据库 实现一个简单的记生词软件 xff0c 基本功能包括 xff0c 添加新的生词及其中文含义 xff0c 浏览已经记录的单词 xff0c 随机选择部分单词进行复习 可考虑其它拓展的功能 这里使用wxpython的基本操作 xff0
  • 数据结构电视大赛投票系统

    电视大赛观众投票及排名系统的设计与实现 8学时 问题描述 xff1a 在很多的电视大赛中 xff0c 通常当选手表演结束后 xff0c 现场观众通过手中的按键对参赛选手进行投票 xff0c 然后对选手获得的票数进行统计 xff0c 从高到低
  • 监督学习与无监督学习

    机器学习如果按照训练样本标签的有无可以分为以下两种常用方法 有监督学习 supervised learning 和无监督学习 unsupervised learning 以机器学习中的分类 classification 来说 xff0c 输
  • Python授权码生成器(密码生成器)

    有时候我们需要为自己的产品设置一些使用权限 xff0c 这就需要随机授权码生成器 当然这是简单的随机生成器 xff0c 像Adobe这种授权码是需要一定的加密算法生成 xff0c 然后再验证授权码是否正确 xff0c 而不是简单的生成 本文
  • C语言赌博机掷骰子

    一个C语言写的小游戏 赌博机 xff0c 适合学习C语言的人学习借鉴 A C language to write a small game gambling machines suitable for learning C language
  • Linux基本命令

    由于参加了机器人社团 xff0c 涉足了Linux的知识 xff0c 今天开始写第一篇关于Linux的文章 Linux简介 xff08 百度百科 xff09 xff1a https baike baidu com item linux 27
  • 如何正确给锂电池充电

    刚给手机换了新电池 xff0c 发现很多人在手机和电脑充电方面存在着误区 xff0c 比如下面这个流言 流言 xff1a 新买的手机必须充满8小时以上并重复几次完全充电放电 xff0c 这个过程叫做激活 xff0c 这样可以使手机的电池达到
  • Python代码刷博客访问量

    寒假闲着无聊自习研究了一下Python爬虫与代理 就发现了一个很简单的刷博客访问量的技巧 首先 xff0c 我们设置一个代理池 xff0c 可以用数据库导入也可以简单的用一个数组 代理池可以从http www xicidaili com 选
  • eclipse svn 分支合并到主干

    首先需厘清SVN的分支以下几个概念 xff1a trunk 主干 可以理解为开发环境的代码 xff0c 平常做开发的工作目录 branches xff1a 从主干拷贝了一份代码重新在svn服务器上的建了个分支目录 通常叫branch xff
  • Python GUI程序整理

    文章目录 GUI程序 Python课程设计Windows系统资源探测器 Python密码存储器 wxpython简单记录生词GUI程序 python微博爬虫GUI程序 Python刷访问量GUI程序 Python弹球游戏 xff08 tki
  • 基于词典的社交媒体内容的情感分析(Python实现)

    之前写了一篇基于NLTK情感预测的文章http www omegaxyz com 2017 12 15 nltk emotion hilite 61 27NLTK 27b 更多内容访问omegaxyz com 情感词典是从微博 新闻 论坛等
  • 在Linux(Ubuntu)下编写编译C语言

    大家都知道在Windows中运行C语言很简单 xff0c 打开一个IDE xff08 VS或者codeblocks xff09 编写代码 xff0c 点击一个按钮就能运行了 在Linux中 xff0c 大家不怎么习惯用IDE xff0c 更

随机推荐

  • 提高C++运行效率的方法

    一 尽量减少值传递 xff0c 多用引用来传递参数 至于其中的原因 xff0c 相信大家也很清楚 xff0c 如果参数是int等语言自定义的类型可能能性能的影响还不是很大 xff0c 但是如果参数是一个类的对象 xff0c 那么其效率问题就
  • 第十三次CCF CSP认证(2018年3月)真题跳一跳

    跳一跳 问题描述 近来 xff0c 跳一跳这款小游戏风靡全国 xff0c 受到不少玩家的喜爱 简化后的跳一跳规则如下 xff1a 玩家每次从当前方块跳到下一个方块 xff0c 如果没有跳到下一个方块上则游戏结束 如果跳到了方块上 xff0c
  • 第十三次CCFCSP认证(2018年3月)真题碰撞的小球

    问题描述 数轴上有一条长度为L xff08 L为偶数 的线段 xff0c 左端点在原点 xff0c 右端点在坐标L处 有n个不计体积的小球在线段上 xff0c 开始时所有的小球都处在偶数坐标上 xff0c 速度方向向右 xff0c 速度大小
  • 第十三次CCF CSP认证(2018年3月)真题URL映射

    问题描述 URL 映射是诸如 Django Ruby on Rails 等网页框架 web frameworks 的一个重要组件 对于从浏览器发来的 HTTP 请求 xff0c URL 映射模块会解析请求中的 URL 地址 xff0c 并将
  • 第十三次CCF CSP认证(2018年3月)真题棋局评估

    问题描述 Alice和Bob正在玩井字棋游戏 井字棋游戏的规则很简单 xff1a 两人轮流往3 3的棋盘中放棋子 xff0c Alice放的是 X xff0c Bob放的是 O xff0c Alice执先 当同一种棋子占据一行 一列或一条对
  • 第十三次CCF CSP认证(2018年3月)真题二次求和

    问题描述 给一棵 n 个节点的树 xff0c 用 1 到 n 的整数表示 每个节点上有一个整数权值 ai 再给出两个整数 L R 现在有 m 个操作 xff0c 每个操作这样描述 xff1a 给定树上两个节点 u v 和一个整数 d xff
  • 演化计算基本方法与思想

    生物系统中 xff0c 进化被认为是一种成功的自适应方法 xff0c 具有很好的健壮性 基本思想 xff1a 达尔文进化论是一种稳健的搜索和优化机制 大多数生物体是通过自然选择和有性生殖进行进化 自然选择决定了群体中哪些个体能够生存和繁殖
  • 发布jar包到Maven中央仓库

    平时自己开发的工具类或者其他的框架的jar包一般都是放在本地 或者把代码上传到github让别人去下载然后自己打包 今天就说说如何把自己的jar包发布到Maven的中央仓库 让其他使用你的jar包的直接去中央仓库下载 如果你用的是阿里云的m
  • 机器学习术语表

    本术语表中列出了一般的机器学习术语和 TensorFlow 专用术语的定义 原文地址 xff1a https developers google cn machine learning glossary hl 61 zh CN A A B
  • Python单下划线与双下划线

    foo 一种约定 Python内部的名字 用来区别其他用户自定义的命名 以防冲突 foo 一种约定 用来指定变量私有 程序员用来指定私有变量的一种方式 foo 这个有真正的意义 解析器用 classname foo来代替这个名字 以区别和其
  • wxpython控件自适应窗口大小

    问题 xff1a wxpython作的窗口 xff0c 点击窗口最大化 xff0c 就会出问题 xff0c 1 窗口控件并不一起最大化 2 最大化后有些控件找不着了 xff0c 再恢复窗口 xff0c 那些消失的控件一样找不到 wxPyth
  • Python统计磁盘代码文件行数

    听说简历要求10万行代码经验 于是写了个Python脚本用来计算电脑磁盘内代码的行数 主要功能 xff1a 统计指定代码的总行数 统计每个代码单独的行数 自定义设置存储路径 设计思路 xff1a 将文件转化为txt文档 xff0c 对txt
  • 第一个Android程序

    Android Studio 是一个Android集成开发工具 xff0c 基于IntelliJ IDEA 类似 Eclipse ADT xff0c Android Studio 提供了集成的 Android 开发工具用于开发和调试 在ID
  • 面向过程与面向对象代码实例对比

    面向过程式代码 xff08 使用数据结构的代码 xff09 便于在不改动既有数据结构的前提下添加新函数 面向对象代码便于在不改动既有函数前提下添加新类 过程式代码难以添加数据结构 xff0c 因为必须修改所有函数 面向对象代码难以添加新函数
  • 机器学习

    文章目录 机器学习基本理论 基本术语与方法特征选择概述多目标优化问题 分类算法 经典算法 分类算法NSGA2算法粒子群优化算法 xff08 PSO xff09 聚类算法蚁群算法 xff08 ACO xff09 其它算法 前沿算法 NSGA2
  • 英文字母信息熵与冗余度计算Python实现

    26个英文字母在平均出现时的信息熵是4 7BIT 而去掉冗余度后的英文字母的信息熵是4 03BIT 公式 H 信息熵 61 Pi log2 Pi Pi 为每个字母在信息中出现的概率 计算公式并不复杂 取以2为底的对数的道理也很简单 因为如果
  • C++静态成员详解

    静态成员是解决同一个类的不同对象之间数据和函数共享问题的 实例属性 xff1a 一个类的所有对象具有相同的属性 xff0c 属性值可以不同 类属性 xff1a 描述类的所有对象共同特征 xff0c 对于任何对象其属性值相同 静态数据成员 用
  • 混淆矩阵简介与Python实现

    什么是混淆矩阵 混淆矩阵是机器学习中总结分类模型预测结果的情形分析表 xff0c 以矩阵形式将数据集中的记录按照真实的类别与分类模型作出的分类判断两个标准进行汇总 这个名字来源于它可以非常容易的表明多个类别是否有混淆 xff08 也就是一个
  • JAVA CPU占用过高问题排查

    1 查找进程 top查看进程占用资源情况 明显看出java的两个进程22714 xff0c 12406占用过高cpu 2 查找线程 使用top H p lt pid gt 查看线程占用情况 3 查找java的堆栈信息 将线程id转换成十六进
  • 【译】Python3.8官方Logging文档(完整版)

    注 xff1a 文章很长 xff0c 约一万字左右 xff0c 可以先收藏慢慢看哟 01 基础部分 日志是用来的记录程序运行事件的工具 当程序员可以通过添加日志打印的代码来记录程序运行过程中发生的某些事件时 这些事件包含了诸如变量数据在内的