如何用 Python 记录日志的!过来教你怎么作!

对一名开发者来讲最糟糕的状况,莫过于要弄清楚一个不熟悉的应用为什么不工做。有时候,你甚至不知道系统运行,是否跟原始设计一致。python

在线运行的应用就是黑盒子,须要被跟踪监控。最简单也最重要的方式就是记录日志。记录日志容许咱们在开发软件的同时,让程序在系统运行时发出信息,这些信息对于咱们和系统管理员来讲都是有用的。程序员

就像为未来的程序员写代码文档同样,咱们应该让新软件产生足够的日志供系统的开发者和管理员使用。日志是关于应用运行状态的系统文件的关键部分。给软件加日志产生句时,要向给将来维护系统的开发者和管理员写文档同样。web

一些纯粹主义者认为一个受过训练的开发者使用日志和测试的时候几乎不须要交互调试器。若是咱们不能用详细的日志解释开发过程当中的应用,那么当代码在线上运行的时候,解释它们会变得更困难。多线程

这篇文章介绍了 Python 的 logging 模块,包括它的设计以及针对更多复杂案例的适用方法。这篇文章不是写给开发者的文档,它更像是一个指导手册,来讲明 Python 的 logging 模板是如何搭建的,而且激发感兴趣的人深刻研究。app

为何使用 logging 模块?

也许会有开发者会问,为何不是简单的 print 语句呢? Logging 模块有不少优点,包括:框架

  1. 多线程支持
  2. 经过不一样级别的日志分类
  3. 灵活性和可配置性
  4. 将如何记录日志与记录什么内容分离

最后一点,将咱们记录内容从记录方式中真正分离,保证了软件不一样部分的合做。举个例子,它容许一个框架或库的开发者增长日志而且让系统管理员或负责运行配置的人员决定稍后应该记录什么。ide

Logging 模块中有什么

Logging 模块完美地将它的每一个部分的职责分离(遵循 Apache Log4j API 的方法)。让咱们看看一个日志线是如何经过这个模块的代码,而且研究下它的不一样部分。函数

记录器(Logger)工具

记录器是开发者常常交互的对象。那些主要的 API 说明了咱们想要记录的内容。测试

举个记录器的例子,咱们能够分类请求发出一条信息,而不用担忧它们是如何从哪里被发出的。

好比,当咱们写下 logger.info(“Stock was sold at %s”, price) 咱们在头脑中就有以下模块:

咱们须要一条线。假设有些代码在记录器中运行,让这条线出如今控制台或文件中。可是在内部实际发生了什么呢?

日志记录

日志记录是 logging 模块用来知足全部需求信息的包。它们包含了须要记录日志的地方、变化的字符串、参数、请求的信息队列等信息。

它们都是被记录的对象。每次咱们调用记录器时,都会生成这些对象。但这些对象是如何序列化到流中的呢?经过处理器!

处理器

处理器将日志记录发送给其余输出终端,他们获取日志记录并用相关函数中处理它们。

好比,一个文件处理器将会获取一条日志记录,而且把它添加到文件中。

标准的 logging 模块已经具有了多种内置的处理器,例如:

多种文件处理器(TimeRotated, SizeRotated, Watched),能够写入文件中

  1. StreamHandler 输出目标流好比 stdout 或 stderr
  2. SMTPHandler 经过 email 发送日志记录
  3. SocketHandler 将日志文件发送到流套接字
  4. SyslogHandler、NTEventHandler、HTTPHandler及MemoryHandler等

目前咱们有个相似于真实状况的模型:

大部分的处理器都在处理字符串(SMTPHandler和FileHandler等)。或许你想知道这些结构化的日志记录是如何转变为易于序列化的字节的。

格式器

格式器负责将丰富的元数据日志记录转换为字符串,若是什么都没有提供,将会有个默认的格式器。

通常的格式器类由 logging 库提供,采用模板和风格做为输入。而后占位符能够在一个 LogRecord 对象中声明全部属性。

好比:’%(asctime)s %(levelname)s %(name)s: %(message)s’ 将会生成日志相似于 2017-07-19 15:31:13,942 INFO parent.child: Hello EuroPython.

请注意:属性信息是经过提供的参数对日志的原始模板进行插值的结果。(好比,对于 logger.info(“Hello %s”, “Laszlo”) 这条信息将会是 “Hello Laszlo”)

全部默认的属性均可以在日志文档中找到。

好了,如今咱们了解了格式器,咱们的模型又发生了变化:

过滤器

咱们日志工具的最后一个对象就是过滤器。

过滤器容许对应该发送的日志记录进行细粒度控制。多种过滤器能同时应用在记录器和处理器中。对于一条发送的日志来讲,全部的过滤器都应该经过这条记录。

用户能够声明他们本身的过滤器做为对象,使用 filter 方法获取日志记录做为输入,反馈 True / False 做为输出。

出于这种考虑,如下是当前的日志工做流:

记录器层级

此时,你可能会对大量复杂的内容和巧妙隐藏的模块配置印象深入,可是还有更须要考虑的:记录器分层。

咱们能够经过 logging.getLogger() 建立一个记录器。这条字符向 getLogger 传递了一个参数,这个参数能够经过使用圆点分隔元素来定义一个层级。

举个例子,logging.getLogger(“parent.child”) 将会建立一个 “child” 的记录器,它的父级记录器叫作 “parent.” 记录器是被 logging 模块管理的全局对象,因此咱们能够方便地在项目中的任何地方检索他们。

记录器的例子一般也被认为是渠道。层级容许开发者去定义渠道和他们的层级。

在日志记录被传递到全部记录器内的处理器时,父级处理器将会进行递归处理,直到咱们到达顶级的记录器(被定义为一个空字符串),或者有一个记录器设置了 propagate = False。咱们可经过更新的图中看出:

请注意父级记录器没有被调用,只有它的处理器被调用。这意味着过滤器和其余在记录器类中的代码不会在父级中被执行。当咱们在记录器中增长过滤器时,这一般是个陷阱。

工做流小结

咱们已经阐明过职责的划分以及咱们是如何微调日志过滤。然而仍是有两个其余的属性咱们没有说起:

  1. 记录器能够是残缺的,从而不容许任何记录从这被发出。
  2. 一个有效的层级能够同时在记录器和处理器中被设置。

举个例子,当一个记录器被设置为 INFO 的等级,只有 INFO 等级及以上的才会被传递,一样的规则适用于处理器。

基于以上全部的考虑,最后的日志记录的流程图看起来像这样:

如何使用日志记录模块

如今咱们已经了解了 logging 模块的部分及设计,是时候去了解一个开发者是如何与它交互的了。如下是一个代码例子:


它用模块 __ name __ 建立了一个日志记录器。它会基于项目结构建立渠道和等级,正如 Pyhon 模块用圆点链接同样。

记录器变量引用记录器的 “module” ,用 “projectA” 做为父级, “root” 做为父级的父级。

在第五行,咱们看到如何执行调用去发送日志。咱们能够用 debug 、 info 、error 或 critical 这些方法之一在合适的等级上去记录日志。

当记录一条信息时,除了模板参数,咱们能够经过特殊的含义传递密码参数,最有意思的是 exc_info 和 stack_info。它们将会分别增长关于当前异常和栈帧的信息。为了方便起见,在记录器对象中有一个方法异常,正如这个错误调用 exc_info=True 。

这些是如何使用记录器模块的基础,可是有些一般被认为是不良操做的作法一样值得说明。

过分格式化字符串

应该尽可能避免使用 loggger.info(“string template {}”.format(argument)) ,可能的话尽可能使用 logger.info(“string template %s”, argument)。 这是个更好的实践,由于只有当日志被发送时,字符串才会发生真正改变。当咱们记录的层级在 INFO 之上时,不这么作会致使浪费周期,由于这个改变仍然会发生。

捕捉和格式化异常

一般,咱们想记录在抓取模块异常的日志信息,若是这样写会很直观:


可是这样的代码会给咱们显示相似于 Something bad happened: “secret_key.” 的日志行,这并非颇有用。若是咱们使用 exc_info 做为事先说明,那么它将会以下显示:


这不只仅会包含异常的准确资源,同时也会包含它的类型。

设置记录器

装备咱们的软件很简单,咱们须要设置日志栈,而且制定这些记录是如何被发出的。

如下是设置日志栈的多种方法

基础设置

这是至今最简单的设置日志记录的方法。使用 logging.basicConfig(level=”INFO”) 搭建一个基础的 StreamHandler ,这样就会记录在 INFO 上的任何东西,而且到控制台以上的级别。如下是编写基础设置的一些参数:

参数 说明 举例
filename 指定建立的文件处理器,使用特定的文件名,而不是流处理器 /var/logs/logs.txt
format 为处理器使用特定格式的字符串 “‘%(asctime)s %(message)s'”
datefmt 使用特定的日期/时间格式 “%H:%M:%S”
level 为根记录器等级设置特定等级 “INFO”

在设置简单的脚本上,这是简单又使用的方法。

请注意, basicConfig 仅仅在运行的一开始能够这么调用。若是你已经设置了你的根记录器,调用 basicConfig 将不会奏效。

字典设置

全部元素的设置以及如何链接它们能够做为字典来讲明。这个字典应当由不一样的部分组成,包括记录器、处理器、格式化以及一些基本的通用参数。

例子以下:


当被引用时, dictConfig 将会禁用全部运行的记录器,除非 disable_existing_loggers 被设置为 false。这一般是须要的,由于不少模块声明了一个全球记录器,它在 dictConfig 被调用以前被导入的时候将会实例化。

你能够查看 schema that can be used for the dictConfig method(连接)。一般,这些设置将会存储在一个 YAML 文件中,而且从那里设置。不少开发者会倾向于使用这种方式而不是使用 fileConfig(连接),由于它为定制化提供了更好的支持。

拓展 logging

幸好设计了这种方式,拓展 logging 模块很容易。让咱们来看些例子:

logging JSON | 记录 JSON

只要咱们想要记录,咱们能够经过建立一种自定义格式化来记录 JSON ,它会将日志记录转化为 JSON 编码的字符串。


添加更多上下文

在格式化中,咱们能够指定任何日志记录的属性。

咱们能够经过多种方式增长属性,在这个例子中,咱们用过滤器来丰富日志记录。


这样有效地在全部日志记录中增长了一个属性,它能够经过记录器。格式化会在日志行中包含这个属性。

请注意这会在你的应用中影响全部的日志记录,包含你可能用到以及你发送日志的库和其余的框架。它能够用来记录相似于在全部日志行里的一个独立请求 ID ,去追踪请求或者去添加额外的上下文信息。

从 Python 3.2 开始,你可使用 setLogRecordFactory 去得到全部日志的建立记录和增长额外的信息。这个 extra attribute 和 LoggerAdapter class 或许一样是有趣的。

缓冲日志

有时候当错误发生时,咱们想要排除日志故障。建立一个缓冲的处理器,来记录当错误发生时的最新故障信息是一种可行的办法。下面的代码是个非人为策划的例子:


更多信息

这篇关于日志记录库的灵活性和可配置性的介绍,目的在于证实它如何设计了分别的关注点的美学。它一样为任何对 logging documentation 和 how-to guide 感兴趣的人提供了一个坚实的基础。虽然这篇文章对于 Python 日志模块并非一个综合性的知道,可是这里有一些针对于常见的问题的回答。

问:个人库发送了一个“ no logger configured” 的警告

答:从 The Hitchhiker’s Guide to Python 查阅 how to configure logging in a library

问:若是一个记录器没有层级设置会怎么样?

答:记录器的有效层级,会由它的父级递归定义。

问:我全部的日志都在本地时间,我如何记录在 UTC ?

答:格式化就是答案!你须要在你的格式化中设置 converter 属性为通用的 UTC 时间。使用 converter = time.gmtime 。

 

此文转载文,著做权归做者全部,若有侵权联系小编删除!

原文地址:https://www.tuicool.com/articles/q2MZniF

 

须要源代码的或者想了解更多的 (点击这里查看)