所谓 logging 就是在软件运行期间跟踪记录特定的 events,开发者可以在代码中调用
logging 提供的方法来标识出某个特定 event。这里的 event 被描述为包含一些变量的
消息,也就说根据 event 不同消息中的变量也不同。它的功能远比函数 print()
那样
只是在 console 中输出一段信息强大。最重要的是开发人员可以自己控制某 events 是
否被记录,这也被称作等级(level)或者严重度(severity)
何时使用 logging
logging 提供了一些很方便的函数: debug(), info(), warning(), error(), critical()
.
需要在特定的情况下分别使用上面的函数:
-
简单向控制台输出一些信息:直接使用
print()
函数 -
记录程序中的某个普通操作:
logging.info()
,如果为了调试而记录还可以使用logging.debug()
-
发出一个警告:
logging.warning()
-
发出一个错误信息:使用
raise
抛出一个异常 -
记录一个错误信息但不抛出异常:
logging.error()
,logging.exception()
或者logging.critical()
logging 的方法在指定等级或者严重性之后才被定义,默认的等级是 WARNING ,下面按照 等级严重性升序进行说明:
-
DEBUG: 调试时输出感兴趣的详细信息。
-
INFO: 确认代码按照预期执行。
-
WARNING: 指出一些代码没有按照预期执行,但整个程序不受影响。
-
ERROR: 遇到更严重的问题,程序不能执行某些函数。
-
CRITICAL: 非常严重的错误,整个程序可能不能继续执行。
由于默认等级是 WARNING 所以说只有比 WARNING 严重的 events 才会被记录。不过我们可以 修改 logging 的默认等级。有多中方式可以记录 logging 其中就常见的就是输出到控制台 或者写入一个磁盘文件。
例子
import logging
# will print a message to console
logging.warning('Watch out!')
# will not print anything
logging.info('I told you so')
# you will see:
# WARNING:root:Watch out!
由于默认等级是 WARNING 所以 logging.info()
并没有打印出来。在打印出来的信息中包括
event 等级以及描述 event 的信息,比如 Watch out! 另外有一个 root ,这个表示在这个
event 发生在程序包的最顶层。
记录到文件
import logging
logging.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')
# your will see in example.log
# DEBUG:root:This message should go to the log file
# INFO:root:So should this
# WARNING:root:And this, too
之所以所有消息都出现在文件中,是因为设置了 level=logging.DEBUG
所以所有 DEBUG 等级以上
消息都会出现,也就是所有消息。需要注意的是 logging.basicConfig()
必须在其他方法调用之
前调用。而且它就像一个一次性的设置装置一样,只有第一次调用才有效,后续调用将会被忽略。
如果多次运行上面的代码会发现,新的消息会添加到文件尾部,如果想每次运行都清除上次的内容
可以像下面这样修改 filemod
logging.basicConfig(filename='example.log', filemod='w', level=logging.DEBUG)
在多个模块中记录
# myapp.py
import logging
import mylib
def 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 logging
def do_something():
logging.info('Doing something')
# you will see
# INFO:root:Started
# INFO:root:Doing something
# INFO:root:Finished
结果跟预想的一样,不过上面的例子中,通过查看 log 信息无法得知信息来自哪个文件的。 有两个方案来解决这个问题:
-
使用 logger 对象。logger 是logging模块中提供的一个类,其实 logging 上记录 log 的函数是通过调用 logger 的实例方法来实现的。每个 logger 对象都一个名字, logger 的名字是由点号隔开的字符串,从而表示一个多级的命名空间。比如一个 logger 的名字是 scan 那么它就是名字为 scan.text scan.pdf 的父对象。按照惯例一般使用
__name__
作为 logger 的名字:logger = logging.getLogger(__name__)
这样相当于 使 logger 名字具有 package/module 的层级。例子:# myapp.py import logging from xx import mylib def main(): logging.basicConfig(filename='myapp.log', level=logging.INFO, filemode='w') logger = logging.getLogger(__name__) logger.info('Started') mylib.do_something() logger.info('Finished')
if name == ‘main’: main()
xx/mylib.py
import logging def do_something(): logger = logging.getLogger(name) logger.info(‘Doing something’)
in myapp.log
INFO:main:Started
INFO:xx.mylib:Doing something
INFO:main:Fin
-
使用
LogRecord
的属性pathname
。
myapp.py
import logging from xx import mylib def main(): logging.basicConfig(filename=’myapp.log’, level=logging.INFO, filemode=’w’, format=’%(levelname)s:%(name)s:%(pathname)s:%(message)s’) logging.info(‘Started’) mylib.do_something() logging.info(‘Finished’) if name == ‘main’: main()
xx/mylib.py
import logging def do_something(): logging.info(‘Doing something’)
in myapp.log
INFO:root:myapp.py:Started
INFO:root:C:\Users\dexbol\Desktop\tt\xx\mylib.py:Doing something
INFO:root:myapp.py:Finished
修改消息显示格式
可以使用 format 属性来定义消息格式,比如:
import logging
logging.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')
# will show
# DEBUG:This message should appear on the console
# INFO:So should this
# WARNING:And this, too
levelname message 这些为 LogRecord 的属性,除了这些外还有:
- %(asctime)s log 时间,一般以这种方式出现: 2012-1-1 12:12:12,890 逗号后面 为毫秒数
- %(created)f log 时间戳,浮点型。
- %(filename)s %(pathname)s 的文件名部分
- %(funcName)s 调用logging时所在的函数名
- %(levelname)s 等级
- %(levelno)d 等级对应的数字
- %(module)s 模块名
- %(msecs)d log 时间的毫秒部分
- %(message)s 消息
- %(name)s logger name
- %(pathname)s 调用logging时所在源码的全路径
- %(process)d 进程id
- %(processName)s 进程名称
- %(relativeCreated)d 从模块加载到记录log的毫秒数
- %(thread)d 线程id
- %(threadName) 进程名称
此外还有 args 与 exc_info 不过这两个属性不需要格式化
修改时间显示格式
首先需要设置 format,使得 format 中包含 %(asctime)s .默认下 asctime 这样显示: 2012-1-1 12:12:12,890 如果要格式这个日期需要设置另外一个属性 datefmt 想这样:
import logging
logging.basicConfig(format='%(asctime)s : %(message)', datefmt='%m/%d/%Y')
logging.warning('is when this event was logged.')
# will show
# 12/12/12 is when this event was logged