开发Python, 一直以来都是使用自己编写的logging模块. 比较土......
今天发现python的标准模块的这个功能做的挺好, 记录一下, 以后使用模块来进行logging.
对于这个模块的介绍网上也很多, 我也不用自己写了, 比较好的如下,
http://crazier9527.iteye.com/blog/290018 Python的标准logging模块
http://blog.endlesscode.com/2010/06/03/python-logging-module/ Python的logging模块
http://docs.python.org/library/logging.html 官方文档
下面就对于在项目中比较需要用到的部分摘录一些,
简单的例子
1 import logging
2 import sys
3 logger = logging.getLogger("endlesscode")
4 formatter = logging.Formatter('%(name)-12s %(asctime)s %(levelname)-8s %(message)s', '%a, %d %b %Y %H:%M:%S',)
5 file_handler = logging.FileHandler("test.log")
6 file_handler.setFormatter(formatter)
7 stream_handler = logging.StreamHandler(sys.stderr)
8 logger.addHandler(file_handler)
9 logger.addHandler(stream_handler)
10 #logger.setLevel(logging.ERROR)
11 logger.error("fuckgfw")
12 logger.removeHandler(stream_handler)
13 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的配置例子的改进:
1 import logging
2 logger = logging.getLogger("simple_example")
3 logger.setLevel(logging.DEBUG)
4 # create file handler which logs even debug messages
5 fh = logging.FileHandler("spam.log")
6 fh.setLevel(logging.DEBUG)
7 # create console handler with a higher log level
8 ch = logging.StreamHandler()
9 ch.setLevel(logging.ERROR)
10 # create formatter and add it to the handlers
11 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
12 ch.setFormatter(formatter)
13 fh.setFormatter(formatter)
14 # add the handlers to logger
15 logger.addHandler(ch)
16 logger.addHandler(fh)
17 # "application" code
18 logger.debug("debug message")
19 logger.info("info message")
20 logger.warn("warn message")
21 logger.error("error message")
22 logger.critical("critical message")
多module使用Logging(只要在同一个Python interpreter process)
上面我曾提到过,所有的对logging.getLogger(‘someLogger’)的调用都会返回同一个对象.这个规则不仅仅在同一个 module有效,而且对在同一个Python的解释器进程里面的多个module也有效.而且,应用代码可以在一个module里面定义一个父 logger,而在另一个module里面继承这个logger,所有对这个子logger的调用都会转到父logger里面去,如下所示:
下面这个是主模块的代码,
1 import logging
2 import auxiliary_module
3 # create logger with "spam_application"
4 logger = logging.getLogger("spam_application")
5 logger.setLevel(logging.DEBUG)
6 # create file handler which logs even debug messages
7 fh = logging.FileHandler("spam.log")
8 fh.setLevel(logging.DEBUG)
9 # create console handler with a higher log level
10 ch = logging.StreamHandler()
11 ch.setLevel(logging.ERROR)
12 # create formatter and add it to the handlers
13 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
14 fh.setFormatter(formatter)
15 ch.setFormatter(formatter)
16 # add the handlers to the logger
17 logger.addHandler(fh)
18 logger.addHandler(ch)
19 logger.info("creating an instance of auxiliary_module.Auxiliary")
20 a = auxiliary_module.Auxiliary()
21 logger.info("created an instance of auxiliary_module.Auxiliary")
22 logger.info("calling auxiliary_module.Auxiliary.do_something")
23 a.do_something()
24 logger.info("finished auxiliary_module.Auxiliary.do_something")
25 logger.info("calling auxiliary_module.some_function()")
26 auxiliary_module.some_function()
27 logger.info("done with auxiliary_module.some_function()")
这个是子模块的代码,
1 import logging
2 # create logger
3 module_logger = logging.getLogger("spam_application.auxiliary")
4 class Auxiliary:
5 def __init__(self):
6 self.logger = logging.getLogger("spam_application.auxiliary.Auxiliary")
7 self.logger.info("creating an instance of Auxiliary")
8 def do_something(self):
9 self.logger.info("doing something")
10 a = 1 + 1
11 self.logger.info("done doing something")
12 def some_function():
13 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.