@dream-cheny
2016-04-16T01:21:50.000000Z
字数 2737
阅读 2522
python
logging 库采用了模块化的设计,提供了许多组件:
Logger 提供应用程序直接使用的接口
Handler 将日志记录发送至合适的目的地
Filter 提供更好的精度控制,可以决定输出哪些日志
Formatter 指明日志最终记录的格式
日志事件信息是以 LogRecord 实例的形式在 Logger, Handler, Filter, Formatter 之间传递。
永远不要直接初始化 Loggers,而应该通过 module-level 函数 logging.getLogger(name)
来得到它,以相同的名称多次调用 getLogger() 将永远返回相同的 Logger 对象的引用。
日志记录是通过调用 Logger 实例的方法实现的,每一个实例都有一个名字,它们在概念上组织成一个层级式的命名空间,用 dots(periods) 作为分割符,例如 'scan' 是 'scan.text' , 'scan.html', 'scan.pdf' 的父Logger。Logger 的名称可以任意命名,用以表示记录的信息是在应用程序的哪个部分产生。
命名 Logger 的一个好的习惯是使用 module-level logger,在每一个模块中使用 logging,命令如下:
logger = logging.getLogger(__name__)
如果添加一个 Handler 到 Logger 和它的一个或多个祖先中,它可能会被多次打印相同的记录。一般来说,不应该将一个 Handler 添加给多个 Logger,如果只将 Handler 添加到合适的 Logger 而它是 Logger 层次中的长老,那么它将看到所有子 Logger 记录的事件。
Logger 每次记录日志都会创建 LogRecord 实例,LogRecord 的这些属性用来将日志数据记录合并到格式化字符串中。下表列出了常用属性名(按名称排序):
属性名 | 格式 | 描述 |
---|---|---|
args | 不需要格式化args | 参数元组,会合并到msg中以产生message |
asctime | %(asctime)s | LogRecord创建的时间,默认形式为 2003-07-08 16:49:45,896 |
filename | %(filename)s | pathname的文件名部分 |
funcName | %(funcName)s | 日志调用所在的函数名 |
levelname | %(levelname)s | 消息的级别名称 DEBUG , INFO , WARNING , ERROR , CRITICAL |
lineno | %(lineno)d | 日志调用所在的源码行号(如果可用) |
module | %(module)s | 模块(文件名的名字部分) |
msecs | %(msecs)d | LogRecord创建时间中的毫秒部分 |
message | %(message)s | 日志消息,msg % args的计算结果。Formatter.format()被调用时该属性被设置。 |
msg | 不需要格式化msg | 日志调用时所传进来的原始的格式化字符串,和args合并以产生message或是任意一个对象(参见使用任意对象作为消息) |
name | %(name)s | 记录日志的logger的名字 |
pathname | %(pathname)s | 日志调用所在文件的完整路径名(如果可用) |
process | %(process)d | 进程 ID (如果可用) |
processName | %(processName)s | 进程名(如果可用) |
thread | %(thread)d | 线程 ID (如果可用) |
threadName | %(threadName)s | 线程名字(如果可用) |
日志级别
从高到低:
级别 | 数值 |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
日志模块被设计成 线程安全
,它使用线程锁来达到该目的,有一个锁被用来同步访问模块的共享数据,每一个 Handler 也创建一个锁来同步访问它底层 I/O
logging.getLogger([name])
返回特定名字的 logger,如果没有名字,返回 logger 层级中根 logger (root logger)。
以相同名字调用该函数总是返回一个 logger 实例,这意味着 logger 实例不需要在应用程序的各个模块之间传来传去。
logging.debug(msg, ...)
logging.info(msg, ...)
logging.warning(msg, ...)
logging.error(msg, ...)
logging.critical(msg, ...)
记录日志。
logging.disable(level)
对所有 logger 提供级别 level,该 level 会覆盖 logger 自己的 level。如果需要临时限制整个应用的日志输出,这个函数就很有用,它会禁用 level 及其以下级别的日志调用。
logging.basicConfig([**kwargs])
创建一个带默认 Formatter 的 StreamHandler,将其加到根 logger,从而配置基本的日志系统。如果根 logger 已经配置了 Handler,则该函数什么都不做。
logging.shutdown()
通知日志系统执行有充的关闭,刷新并关闭所有的 Handler,在应用程序退出的时候调用该函数,调用该函数后不应该继续使用日志系统。
在开发库的时候需要使用 logging,需要用文档记录如何使用日志。如果应该程序和库没有配置 logging,而库中调用了 logging,则可能会出现两种行为(取决于 python 的版本):
1. 如果 logging.raiseExceptions == False
(产品模式),事件被默默丢弃
2. 如果 logging.raiseExceptions == True
(开发模式),输出消息 No handlers could be found for logger "x.y.z"
(输出一次)
如果因为某种原因,不希望这个消息在没有任何 logging 配置的情况下被打印,可以给库的 top-level logger 绑定一个什么也不做的 handler:NullHandler
NullHandler
存在于 logging 包中,实际上也是一个 handler,只是什么都不输出而已。
一般应用形式:
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
强烈建议对库中的 logger 只添加
NullHandler
。因为配置 handlers 是使用库的应用程序开发者的特权。应用程序开发者了解目标用户,也知道哪些 handler 最适合,如果在库中暗自添加了处理器,也许会影响到开发都使用库。