Logging 即是追踪一些软件运行时发生的事件。软件的开发人员在代码中增加了 logging 的调用用来确定某些事件的发生。事件通过一些描述性消息描述,这些消息可能会包含一些变量数据(不同的事件发生时有不同的数据)。开发者同样考虑事件的重要性,这个重要性也可以称之为级别或严重性。
When to use logging
Logging 为一些简单的日志记录提供了很多很方便的函数。它们是 debug(),info(),warngin(),error() 和 critical()。下表介绍了什么时候应该使用 logging,它说明了对于一组公共的任务可使用的最好的工具。
你想要执行的任务 | 对于这些任务最好的工具 |
---|---|
命令行脚本或者程序的常规使用:控制台输出显示 | print() |
报告程序正常运行期间发生的事件(比如监控状态或者是故障调查) | logging.info()(或者是 logging().debug() 输出更详细的信息来进行诊断) |
对一些特定的运行事件发出警告 | warnings.war() 如果可以避免这个问题,则应该修改客户端应用程序以消除警告。logging.warning() 如果客户端应用程序对于这种情况无能为力,但事件仍然值注意 |
对一些特定的运行事件报告错误 | 抛出异常 |
在不抛出异常的情况下对错误的抑制(例如长时间运行状态下的服务器进行中的错误处理程序) | logging.error(),logging.exception() 或者是 logging.critical() 适用于特定的错误或应用程序领域 |
这些日志记录函数是以它们用来跟踪的事件的级别或者严重性来命名的。下面是对标准的级别及适用性的描述(按照严重程序递增的顺序):
级别 | 适用条件 |
---|---|
DEBUG | 详细信息,通常仅在诊断问题时使用 |
INFO | 确认一切正常 |
WARNING | 暗示有意外情况发生或未来即将发生某些问题(比如:磁盘空间不足)。但软件仍然正常运行 |
ERROR | 因为一些严重的问题,软件的部分功能不能正常执行 |
CRITICAL | 很严重的错误,意味着程序本可能不能继续运行 |
默认级别是 WARNING,意味着只有这个级别的事件或者高于这些级别的事件才能被追踪,除非 logging 包配置为可执行其他操作。
跟踪的事件可以以不同的方式处理。处理跟踪事件的最简单方法是将它们打印到控制台。另一种常见的方法是将它们写入磁盘文件。
A simple example
一个非常简单的例子,如下所示:
代码语言:javascript复制import logging
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything
如果你将上面的代码写入脚本并且运行,你将会看到:
代码语言:javascript复制WARNING:root:Watch out!
在控制台上打印出了信息。INFO 的信息没有出现是因为默认的级别是 WARNING。打印的信息中包括了级别、对于事件的描述,比如"Watch out!"。现在不要纠结 "root" 这部分信息,后面将会解释。实际上输出可以根据你的需要很灵活的进行格式化,格式化也将在后面进行解释。
Logging to a file
一种非常常见的情况是将日志记录事件记录在文件中,因此接下来让我们看一下。确保在新启动的Python解释器中尝试以下操作,而不仅仅是在上面的代码基础上进行更改:
代码语言:javascript复制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')
现在如果我们打开日志文件并查看下文件内容,我们可以发现如下日志信息:
代码语言:javascript复制DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
这个例子同样展示的你可以怎样设置日志的级别,就好像设置要跟踪的阈值一样。在这个例子中,我们设置阈值为 DEBUG,因此所有级别的信息都将会输出。
如果你想通过命令行选项来设置日志的级别,像如下形式:
代码语言:javascript复制--log=INFO
并且代码中你已经有了变量比如叫 loglevel 的用于接受 --log 传递进来的值,则你可以通过使用:
代码语言:javascript复制getattr(logging, loglevel.upper())
来获取你通过 level 参数传递给 basicConfig() 的值。你可以想要对用户输入进行错误检查,参考代码如下所示:
代码语言:javascript复制# 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=debug
numeric_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()等的任何调用。由于它是一次性的简单配置工具,因此实际上只有第一个调用会做任何事情:后续调用实际上是无操作的。
如果您多次运行上述脚本,则连续运行的消息将附加到文件example.log中。如果希望每个运行重新开始,而不保留旧的信息,则可以通过将上述示例中的调用更改为以下方式来指定filemode参数:
代码语言:javascript复制logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)
输出将与以前相同,但是不再将日志文件附加到该文件,因此先前运行的消息将丢失。
Logging from multiple modules
如果你的程序包含多个模块,下面是一个你可以在这种情况下如何组织日志信息的例子:
代码语言:javascript复制# 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()
代码语言:javascript复制# mylib.py
import logging
def do_something():
logging.info('Doing something')
如果你运行 myapp.py,你在 myapp.log 中看到如下信息:
代码语言:javascript复制INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
上面的信息是你希望看到的。你可以将 mylib.py 中的模式推广到多个模块中使用。需要注意的是,这种简单的使用模式,除了查看事件描述之后,你无法知道消息是来自于程序了哪个位置。如果你想要追踪信息的来源位置,你需要参考其他教程:Advanced Logging Tutorial。
Logging variable data
使用一个格式化的字符串来用于事件描述,同时添加可变数据来作为参数,以此来记录可变数据。例如:
代码语言:javascript复制import logging
logging.warning('%s before you %s', 'Look', 'leap!')
将会展示:
代码语言:javascript复制WARNING:root:Look before you leap!
正如你所看到的,通过使用旧的 %s 的方式,将可变的数据合并到事件信息中。这是为了向后兼容:logging 包早于较新的格式设置选项,比如:str.format() 和 string.Template。支持这些新的格式设置选项,但对它们的探索不在本教程的范围之内:查看 Using particular formatting styles throughout your application
Changing the format of displayed messages
修改要展示的信息的格式,你需要指定你想要使用的格式:
代码语言:javascript复制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')
输出:
代码语言:javascript复制DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too
注意,"root"在之前的例子中是有出现的。对于格式化字符串可展示的所有信息,可以参考LogRecord属性的文档,但是为了简单使用,您只需要级别名称(严重性),消息(事件描述,包括变量数据)并可能需要显示 事件发生的时间。下一节将对此进行描述。
Displaying the date/time in messages
为了展示事件中的 date 和 time,你需要在格式化字符串中使用 "%(asctime)s":
代码语言:javascript复制import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
默认的 date/time 的格式(上面已经展示过了)是 ISO8601 or RFC 3339。如果你需要更多的控制时间的展示格式,提交一个 datefmt 参数给 basicConfig 即可,如下所示:
代码语言:javascript复制import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
输出如下:
代码语言:javascript复制12/12/2010 11:46:36 AM is when this event was logged.
点击阅读原文,可查看英文链接。
文章好看点这里