python logging
开发Python, 一直以来都是使用自己编写的logging模块. 比较土……
今天发现python的标准模块的这个功能做的挺好, 记录一下, 以后使用模块来进行logging.
对于这个模块的介绍网上也很多, 我也不用自己写了, 比较好的如下,
- import logging
- import sys
- logger = logging.getLogger(“endlesscode”)
- formatter = logging.Formatter(‘%(name)-12s %(asctime)s %(levelname)-8s %(message)s’, ’%a, %d %b %Y %H:%M:%S’,)
- file_handler = logging.FileHandler(“test.log”)
- file_handler.setFormatter(formatter)
- stream_handler = logging.StreamHandler(sys.stderr)
- logger.addHandler(file_handler)
- logger.addHandler(stream_handler)
- #logger.setLevel(logging.ERROR)
- logger.error(“fuckgfw”)
- logger.removeHandler(stream_handler)
- logger.error(“fuckgov”)
上面这段代码基本包含logging模块的基本feature
GetLogger
GetLogger() returns a reference to a logger instance with the specified name if it is provided, or root if not. The names are period-separated hierarchical structures. Multiple calls to getLogger() with the same name will return a reference to the same logger object.后面会看到这种以’.‘分隔的hierarchical structures有什么用.
Formatter
Formatter对象定义了最终log信息的顺序,结构和内容, 后面会详细解释.
Handler
这儿用到了StreamHandler和FileHandler, 用于向不同的输出端打log.
SetLevel
Logging有如下级别: DEBUG,INFO,WARNING,ERROR,CRITICAL
默认级别是WARNING, logging模块只会输出指定level以上的log
这样的好处, 就是在项目开发时debug用的log, 在产品release阶段不用一一注释, 只需要调整logger的级别就可以了, 很方便的.
Formatter
Formatter对象定义了最终log信息的顺序,结构和内容.于基本的logging.Handler类不同,应用可以直接实例化 formatter类,当然,如果需要你也可以子例化formatter以便定制它的一些行为.构造函数接受两个可选参数:一个信息格式字符串和一个日期 格式字符串.如果没有信息格式字符串,直接输出log信息.如果没有日期格式字符串,默认的格式是:%Y-%m-%d %H:%M:%S
上面的代码给出了Formatter的例子, 下面表格给出所有可以使用的format,
Handler
Logging包含很多handler, 可能用到的有下面几种
- StreamHandler instances send error messages to streams (file-like objects).
- FileHandler instances send error messages to disk files.
- RotatingFileHandler instances send error messages to disk files, with support for maximum log file sizes and log file rotation.
- TimedRotatingFileHandler instances send error messages to disk files, rotating the log file at certain timed intervals.
- SocketHandler instances send error messages to TCP/IP sockets.
- DatagramHandler instances send error messages to UDP sockets.
- SMTPHandler instances send error messages to a designated email address.
最常用的也就是StreamHandler和FileHandler
Configuration
- Creating loggers, handlers, and formatters explicitly using Python code that calls the configuration methods listed above.
- Creating a logging config file and reading it using the fileConfig() function.
- Creating a dictionary of configuration information and passing it to the dictConfig() function.
第一种配置方法前面的code里面已经有了
第二种配置方法, 我觉得在项目里面是比较实用的, 通过编写配置文件, 在code里面只需要用fileConfig配置一下logging, 显得比较简洁.
这个可以参照 http://crazier9527.iteye.com/blog/290026 或 官方文档.
Multiple handlers and formatters
Loggers是一个简单的Python对象.addHandler()方法没有最多或者最少配额,当你的应用需要在把所有的log信息打到一 个txt文件中去,同时又需要把errors级别一上的错误信息打到console时,你就会体会到这个特性的好处.只要简单的配置一下合适的 handlers就可以实现这个功能.应用对logging的调用用不着修改.以下是对前一个基于module的配置例子的改进:
[python] view plain copy
- import logging
- logger = logging.getLogger(“simple_example”)
- logger.setLevel(logging.DEBUG)
- # create file handler which logs even debug messages
- fh = logging.FileHandler(“spam.log”)
- fh.setLevel(logging.DEBUG)
- # create console handler with a higher log level
- ch = logging.StreamHandler()
- ch.setLevel(logging.ERROR)
- # create formatter and add it to the handlers
- formatter = logging.Formatter(“%(asctime)s - %(name)s - %(levelname)s - %(message)s”)
- ch.setFormatter(formatter)
- fh.setFormatter(formatter)
- # add the handlers to logger
- logger.addHandler(ch)
- logger.addHandler(fh)
- # ”application” code
- logger.debug(“debug message”)
- logger.info(“info message”)
- logger.warn(“warn message”)
- logger.error(“error message”)
- logger.critical(“critical message”)
多module使用Logging(只要在同一个Python interpreter process)
上面我曾提到过,所有的对logging.getLogger(‘someLogger’)的调用都会返回同一个对象.这个规则不仅仅在同一个 module有效,而且对在同一个Python的解释器进程里面的多个module也有效.而且,应用代码可以在一个module里面定义一个父 logger,而在另一个module里面继承这个logger,所有对这个子logger的调用都会转到父logger里面去,如下所示:
下面这个是主模块的代码,
[python] view plain copy
- import logging
- import auxiliary_module
- # create logger with ”spam_application”
- logger = logging.getLogger(“spam_application”)
- logger.setLevel(logging.DEBUG)
- # create file handler which logs even debug messages
- fh = logging.FileHandler(“spam.log”)
- fh.setLevel(logging.DEBUG)
- # create console handler with a higher log level
- ch = logging.StreamHandler()
- ch.setLevel(logging.ERROR)
- # create formatter and add it to the handlers
- formatter = logging.Formatter(“%(asctime)s - %(name)s - %(levelname)s - %(message)s”)
- fh.setFormatter(formatter)
- ch.setFormatter(formatter)
- # add the handlers to the logger
- logger.addHandler(fh)
- logger.addHandler(ch)
- logger.info(“creating an instance of auxiliary_module.Auxiliary”)
- a = auxiliary_module.Auxiliary()
- logger.info(“created an instance of auxiliary_module.Auxiliary”)
- logger.info(“calling auxiliary_module.Auxiliary.do_something”)
- a.do_something()
- logger.info(“finished auxiliary_module.Auxiliary.do_something”)
- logger.info(“calling auxiliary_module.some_function()”)
- auxiliary_module.some_function()
- logger.info(“done with auxiliary_module.some_function()”)
这个是子模块的代码,
[python] view plain copy
- import logging
- # create logger
- module_logger = logging.getLogger(“spam_application.auxiliary”)
- class Auxiliary:
- def __init__(self):
- self.logger = logging.getLogger(“spam_application.auxiliary.Auxiliary”)
- self.logger.info(“creating an instance of Auxiliary”)
- def do_something(self):
- self.logger.info(“doing something”)
- a = 1 + 1
- self.logger.info(“done doing something”)
- def some_function():
- module_logger.info(“received a call to /”some_function/”")
可以看到, 我们在主模块里面定义了一个logger ‘spam_application’, 并对他进行了配置.
那么在这个解释器进程里面的任何地方去通过getLogger(‘spam_application’)得到的对象都是一样的, 不需要从新定义配置, 可以直接使用.
更方便的是, 你定义任意该logger的子logger, 都可以共享父logger的定义和配置
所谓的父子logger只是简单的通过命名来识别, 任意以’spam_application.’开头的logger都是他的子logger, 例如’spam_application.auxiliary’
这个在实际的开发中, 还是很方便的, 对于一个application,
首先通过logging配置文件编写好这个application所对应的log策略, 可以只生成一个根logger, 比如叫’Project’
然后在Main函数里面, 通过fileConfig加载logging的配置
接着在appliction的任意地方, 不同的模块中, 可以使用Project的子logger, 如Project.UI, Project.Core, 来进行log, 并且不需要反复的定义和配置各个logger.
如果使用Python写一个比较大型的程序,你一定会用上日志系统。特别是Python这样的动态语言,很多错误都只能在运行的时候才能发现, 一个 好的日志系统对于Python程序相当重要。最简单的解决方案当然是直接使用print输出运行信息。但是这样太简单了,没有分级功能,如果在发布的时候 想去掉调试用的运行信息还得找出所有的print语句进行修改。再者,print只能输出到控制台,想要输出到文件或者通过电子邮件发送到其他地方,一个 print语句就没办法解决了。通过使用Python的日志系统,就可以解决以上问题。
首先看一下这个示例:
import logging
LOG=logging.getLogger(’应用程序名’)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
LOG.addHandler(console)
LOG.debug(’调试信息’)
LOG.info(’有用的信息’)
LOG.warning(’警告信息’)
LOG.error(’错误信息’)
LOG.critical(’严重错误信息’)
上面的代码想控制台输出了五种错误信息。分为五个从低到高的级别,从DEBUG到CRITICAL。此外,我们还指定了程序输出的级别,只有INFO级别以上的信息才会被输出。
这就是日志系统最常用的使用方法。这段代码中有两个概念可以帮助我们更进一步使用Python的日志系统:
1. “Logger”。每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模块可以这样:
LOG=logging.getLogger(”chat.kernel”)
我们接下来可以看到使用这种命名方法的用途。
2. “Handler”。用于处理程序的输出。 Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。所有的Handler可以支持三个操作:
1. 设置输出格式。比如设置输出的信息中包含时间和级别信息:
LOG=logging.getLogger(”chat.gui”)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter(’%(asctime)s %(levelname)s %(message)s’)
console.setFormatter(formatter)
LOG.addHandler(console)
熟悉Python的朋友应该会发现,logging.Formatter的参数实际上只是Python常用的“%”字符串格式化。它使用“%(name)s”表示占位符。下面是一个完整的表格,展示了日志系统可以输出的各种信息:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是“2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息
2. 设置输出级别
在上面我们已经演示了如何设置输出级别了。除了Python内置的五种级别,我们还可以自定义输出级别。
TODO 子定义输出级别
3. 设置过滤器
细心的朋友一定会发现前文调用logging.getLogger()时参数的格式类似于“A.B.C”。采取这样的格式其实就是为了可以配置过滤器。看一下这段代码:
LOG=logging.getLogger(”chat.gui.statistic”)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter(’%(asctime)s %(levelname)s %(message)s’)
console.setFormatter(formatter)
filter=logging.Filter(”chat.gui”)
console.addFilter(filter)
LOG.addHandler(console)
和前面不同的是我们在Handler上添加了一个过滤器。现在我们输出日志信息的时候就会经过过滤器的处理。名为“A.B”的过滤器只让名字带 有 “A.B”前缀的Logger输出信息。可以添加多个过滤器,只要有一个过滤器拒绝,日志信息就不会被输出。另外,在Logger中也可以添加过滤器。
每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:
1) logging.StreamHandler
使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr
2) 2.logging.FileHandler
和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名
mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a’,即添加到文件末尾。
3) 3.logging.handlers.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名, 然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler一样。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
4) 4.logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一 定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
S 秒M 分H 小时D 天W 每星期(interval==0时代表星期一)midnight 每天凌晨
5) 5.logging.handlers.SocketHandler
6) 6.logging.handlers.DatagramHandler
以上两个Handler类似,都是将日志信息发送到网络。不同的是前者使用TCP协议,后者使用UDP协议。它们的构造函数是:
Handler(host, port)
其中host是主机名,port是端口名
7) 7.logging.handlers.SysLogHandler
8) 8.logging.handlers.NTEventLogHandler
9) 9.logging.handlers.SMTPHandler
10) 10.logging.handlers.MemoryHandler
11) 11.logging.handlers.HTTPHandler
这些我自己没用过,期待大家补充。或者参考Python的使用手册
Tagged:python