開發(fā)Python, 一直以來都是使用自己編寫的logging模塊. 比較土......
今天發(fā)現(xiàn)python的標(biāo)準(zhǔn)模塊的這個(gè)功能做的挺好, 記錄一下, 以后使用模塊來進(jìn)行l(wèi)ogging.
對于這個(gè)模塊的介紹網(wǎng)上也很多, 我也不用自己寫了, 比較好的如下,
http://crazier9527.iteye.com/blog/290018 Python的標(biāo)準(zhǔn)logging模塊
http://blog.endlesscode.com/2010/06/03/python-logging-module/ Python的logging模塊
http://docs.python.org/library/logging.html 官方文檔
下面就對于在項(xiàng)目中比較需要用到的部分摘錄一些,
簡單的例子
[python]
view plaincopyimport 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信息的順序,結(jié)構(gòu)和內(nèi)容, 后面會詳細(xì)解釋.
Handler
這兒用到了StreamHandler和FileHandler, 用于向不同的輸出端打log.
SetLevel
Logging有如下級別: DEBUG,INFO,WARNING,ERROR,CRITICAL
默認(rèn)級別是WARNING, logging模塊只會輸出指定level以上的log
這樣的好處, 就是在項(xiàng)目開發(fā)時(shí)debug用的log, 在產(chǎn)品release階段不用一一注釋, 只需要調(diào)整logger的級別就可以了, 很方便的.
Formatter
Formatter對象定義了最終log信息的順序,結(jié)構(gòu)和內(nèi)容.于基本的logging.Handler類不同,應(yīng)用可以直接實(shí)例化formatter類,當(dāng)然,如果需要你也可以子例化formatter以便定制它的一些行為.構(gòu)造函數(shù)接受兩個(gè)可選參數(shù):一個(gè)信息格式字符串和一個(gè)日期格式字符串.如果沒有信息格式字符串,直接輸出log信息.如果沒有日期格式字符串,默認(rèn)的格式是:%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里面已經(jīng)有了
第二種配置方法, 我覺得在項(xiàng)目里面是比較實(shí)用的, 通過編寫配置文件, 在code里面只需要用fileConfig配置一下logging, 顯得比較簡潔.
這個(gè)可以參照
http://crazier9527.iteye.com/blog/290026 或 官方文檔.
Multiple handlers and formatters
Loggers是一個(gè)簡單的Python對象.addHandler()方法沒有最多或者最少配額,當(dāng)你的應(yīng)用需要在把所有的log信息打到一個(gè)txt文件中去,同時(shí)又需要把errors級別一上的錯(cuò)誤信息打到console時(shí),你就會體會到這個(gè)特性的好處.只要簡單的配置一下合適的handlers就可以實(shí)現(xiàn)這個(gè)功能.應(yīng)用對logging的調(diào)用用不著修改.以下是對前一個(gè)基于module的配置例子的改進(jìn):
[python]
view plaincopyimport 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(只要在同一個(gè)Python interpreter process)
上面我曾提到過,所有的對logging.getLogger(‘someLogger’)的調(diào)用都會返回同一個(gè)對象.這個(gè)規(guī)則不僅僅在同一個(gè)module有效,而且對在同一個(gè)Python的解釋器進(jìn)程里面的多個(gè)module也有效.而且,應(yīng)用代碼可以在一個(gè)module里面定義一個(gè)父logger,而在另一個(gè)module里面繼承這個(gè)logger,所有對這個(gè)子logger的調(diào)用都會轉(zhuǎn)到父logger里面去,如下所示:
下面這個(gè)是主模塊的代碼,
[python]
view plaincopyimport 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()")
這個(gè)是子模塊的代碼,
[python]
view plaincopyimport 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/"")
可以看到, 我們在主模塊里面定義了一個(gè)logger 'spam_application', 并對他進(jìn)行了配置.
那么在這個(gè)解釋器進(jìn)程里面的任何地方去通過getLogger('spam_application')得到的對象都是一樣的, 不需要從新定義配置, 可以直接使用.
更方便的是, 你定義任意該logger的子logger, 都可以共享父logger的定義和配置
所謂的父子logger只是簡單的通過命名來識別, 任意以'spam_application.'開頭的logger都是他的子logger, 例如'spam_application.auxiliary'
這個(gè)在實(shí)際的開發(fā)中, 還是很方便的, 對于一個(gè)application,
首先通過logging配置文件編寫好這個(gè)application所對應(yīng)的log策略, 可以只生成一個(gè)根logger, 比如叫'Project'
然后在Main函數(shù)里面, 通過fileConfig加載logging的配置
接著在appliction的任意地方, 不同的模塊中, 可以使用Project的子logger, 如Project.UI, Project.Core, 來進(jìn)行l(wèi)og, 并且不需要反復(fù)的定義和配置各個(gè)logger.
如果使用Python寫一個(gè)比較大型的程序,你一定會用上日志系統(tǒng)。特別是Python這樣的動(dòng)態(tài)語言,很多錯(cuò)誤都只能在運(yùn)行的時(shí)候才能發(fā)現(xiàn),一個(gè) 好的日志系統(tǒng)對于Python程序相當(dāng)重要。最簡單的解決方案當(dāng)然是直接使用print輸出運(yùn)行信息。但是這樣太簡單了,沒有分級功能,如果在發(fā)布的時(shí)候 想去掉調(diào)試用的運(yùn)行信息還得找出所有的print語句進(jìn)行修改。再者,print只能輸出到控制臺,想要輸出到文件或者通過電子郵件發(fā)送到其他地方,一個(gè) print語句就沒辦法解決了。
通過使用Python的日志系統(tǒng),就可以解決以上問題。
首先看一下這個(gè)示例:
import logging
LOG=logging.getLogger(’應(yīng)用程序名’)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
LOG.addHandler(console)
LOG.debug(’調(diào)試信息’)
LOG.info(’有用的信息’)
LOG.warning(’警告信息’)
LOG.error(’錯(cuò)誤信息’)
LOG.critical(’嚴(yán)重錯(cuò)誤信息’)
上面的代碼想控制臺輸出了五種錯(cuò)誤信息。分為五個(gè)從低到高的級別,從DEBUG到CRITICAL。此外,我們還指定了程序輸出的級別,只有INFO級別以上的信息才會被輸出。
這就是日志系統(tǒng)最常用的使用方法。這段代碼中有兩個(gè)概念可以幫助我們更進(jìn)一步使用Python的日志系統(tǒng):
1. “Logger”。每個(gè)程序在輸出信息之前都要獲得一個(gè)Logger。Logger通常對應(yīng)了程序的模塊名,比如聊天工具的圖形界面模塊可以這樣獲得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模塊可以這樣:
LOG=logging.getLogger(”chat.kernel”)
我們接下來可以看到使用這種命名方法的用途。
2. “Handler”。用于處理程序的輸出。 Python的日志系統(tǒng)有多種Handler可以使用。有些Handler可以把信息輸出到控制臺,有些Logger可以把信息輸出到文件,還有些 Handler可以把信息發(fā)送到網(wǎng)絡(luò)上。如果覺得不夠用,還可以編寫自己的Handler。
所有的Handler可以支持三個(gè)操作:
1. 設(shè)置輸出格式。比如設(shè)置輸出的信息中包含時(shí)間和級別信息:
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的朋友應(yīng)該會發(fā)現(xiàn),logging.Formatter的參數(shù)實(shí)際上只是Python常用的“%”字符串格式化。它使用“%(name)s”表示占位符。下面是一個(gè)完整的表格,展示了日志系統(tǒng)可以輸出的各種信息:
%(name)s Logger的名字
%(levelno)s 數(shù)字形式的日志級別
%(levelname)s 文本形式的日志級別
%(pathname)s 調(diào)用日志輸出函數(shù)的模塊的完整路徑名,可能沒有
%(filename)s 調(diào)用日志輸出函數(shù)的模塊的文件名
%(module)s 調(diào)用日志輸出函數(shù)的模塊名
%(funcName)s 調(diào)用日志輸出函數(shù)的函數(shù)名
%(lineno)d 調(diào)用日志輸出函數(shù)的語句所在的代碼行
%(created)f 當(dāng)前時(shí)間,用UNIX標(biāo)準(zhǔn)的表示時(shí)間的浮點(diǎn)數(shù)表示
%(relativeCreated)d 輸出日志信息時(shí)的,自Logger創(chuàng)建以來的毫秒數(shù)
%(asctime)s 字符串形式的當(dāng)前時(shí)間。默認(rèn)格式是“2003-07-08 16:49:45,896”。逗號后面的是毫秒
%(thread)d 線程ID??赡軟]有
%(threadName)s 線程名??赡軟]有
%(process)d 進(jìn)程ID??赡軟]有
%(message)s 用戶輸出的消息
2. 設(shè)置輸出級別
在上面我們已經(jīng)演示了如何設(shè)置輸出級別了。除了Python內(nèi)置的五種級別,我們還可以自定義輸出級別。
TODO 子定義輸出級別
3. 設(shè)置過濾器
細(xì)心的朋友一定會發(fā)現(xiàn)前文調(diào)用logging.getLogger()時(shí)參數(shù)的格式類似于“A.B.C”。采取這樣的格式其實(shí)就是為了可以配置過濾器。看一下這段代碼:
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上添加了一個(gè)過濾器?,F(xiàn)在我們輸出日志信息的時(shí)候就會經(jīng)過過濾器的處理。名為“A.B”的過濾器只讓名字帶有 “A.B”前綴的Logger輸出信息??梢蕴砑佣鄠€(gè)過濾器,只要有一個(gè)過濾器拒絕,日志信息就不會被輸出。另外,在Logger中也可以添加過濾器。
每個(gè)Logger可以附加多個(gè)Handler。接下來我們就來介紹一些常用的Handler:
1) logging.StreamHandler
使用這個(gè)Handler可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息。它的構(gòu)造函數(shù)是:
StreamHandler([strm])
其中strm參數(shù)是一個(gè)文件對象。默認(rèn)是sys.stderr
2) 2.logging.FileHandler
和StreamHandler類似,用于向一個(gè)文件輸出日志信息。不過FileHandler會幫你打開這個(gè)文件。它的構(gòu)造函數(shù)是:
FileHandler(filename[,mode])
filename是文件名,必須指定一個(gè)文件名
mode是文件的打開方式。參見Python內(nèi)置函數(shù)open()的用法。默認(rèn)是’a',即添加到文件末尾。
3) 3.logging.handlers.RotatingFileHandler
這個(gè)Handler類似于上面的FileHandler,但是它可以管理文件大小。當(dāng)文件達(dá)到一定大小之后,它會自動(dòng)將當(dāng)前日志文件改名,然后創(chuàng)建 一個(gè)新的同名日志文件繼續(xù)輸出。比如日志文件是chat.log。當(dāng)chat.log達(dá)到指定的大小之后,RotatingFileHandler自動(dòng)把 文件改名為chat.log.1。不過,如果chat.log.1已經(jīng)存在,會先把chat.log.1重命名為chat.log.2。。。最后重新創(chuàng)建 chat.log,繼續(xù)輸出日志信息。它的構(gòu)造函數(shù)是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個(gè)參數(shù)和FileHandler一樣。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes為0,意味著日志文件可以無限大,這時(shí)上面描述的重命名過程就不會發(fā)生。
backupCount用于指定保留的備份文件的個(gè)數(shù)。比如,如果指定為2,當(dāng)上面描述的重命名過程發(fā)生時(shí),原有的chat.log.2并不會被更名,而是被刪除。
4) 4.logging.handlers.TimedRotatingFileHandler
這個(gè)Handler和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時(shí)重新創(chuàng)建日志文件,而是間隔一定時(shí)間就 自動(dòng)創(chuàng)建新的日志文件。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數(shù)字,而是當(dāng)前時(shí)間。它的構(gòu)造函數(shù)是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數(shù)和backupCount參數(shù)和RotatingFileHandler具有相同的意義。
interval是時(shí)間間隔。
when參數(shù)是一個(gè)字符串。表示時(shí)間間隔的單位,不區(qū)分大小寫。它有以下取值:
S 秒M 分H 小時(shí)D 天W 每星期(interval==0時(shí)代表星期一)midnight 每天凌晨
5) 5.logging.handlers.SocketHandler
6) 6.logging.handlers.DatagramHandler
以上兩個(gè)Handler類似,都是將日志信息發(fā)送到網(wǎng)絡(luò)。不同的是前者使用TCP協(xié)議,后者使用UDP協(xié)議。它們的構(gòu)造函數(shù)是:
Handler(host, port)
其中host是主機(jī)名,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
這些我自己沒用過,期待大家補(bǔ)充。或者參考Python的使用手冊