1. logging模塊中基本的幾個類
圖中是涉及到的類的繼承關係,下面挑一些關鍵類進行講解。
LogRecord
這是一個記錄日誌信息的類,構造這樣的一個對象,依據其__init__(self, name, level, pathname, lineno,msg, args, exc_info, func=None)需要傳入的參數依次是生成該條日誌logger的名字,該條日誌的等級,輸出該日誌語句所在文件的路徑,輸出該日誌語句所在的行號,日誌字符串,參數,異常信息以及輸出該日誌語句所在的函數!其中args是tuple形式,元素可以是基本類型,比如int,float,string,也可以是dict。舉個例子,msg = "a %(a)d b %(b)s",args = ({'a':1, 'b':2})。爲了方便應用(構造Formatter以及Filter),介紹一下該類中的幾個成員變量:
變量名 |
描述 |
構造Formatter串對應項 |
self.name |
生成該日誌logger的名字 |
%(name)s |
self.levelno |
日誌等級的數值表示 |
%(levelno)s |
self.levelname |
日誌等級的字符串表示 |
%(levelname)s |
self.pathname |
輸出該日誌其語句所在文件的路徑 |
%(pathname)s |
self.filename |
self.pathname中得文件名 |
%(filename)s |
self.module |
輸出該日誌其語句所在的模塊名 |
%(module)s |
self.lineno |
輸出該日誌語句其語句所在的行號 |
%(lineno)d |
self.funcName |
輸出該日誌其語句所在的函數 |
%(funcName)s |
self.created |
日誌被創建的時間(Epoch時間,time.time()的返回值) |
%(created)f |
self.msecs |
self.created的小數部位乘以1000(毫秒單位) |
%(msecs)d |
self.relativeCreated |
日誌被創建時間與日誌模塊被加載時間的時間差(毫秒單位) |
%(relativeCreated)d |
self.thread |
創建該日誌線程的ID |
%(thread)d |
self.threadName |
創建該日誌線程的名字 |
%(threadName)s |
self.process |
創建該日誌進程的ID |
%(process)d |
self.processName |
創建該日誌進程的名字 |
%(processName)s(文檔裏並沒有給出,似乎是由於logging不支持多進程訪問一個文件【3】,意味着沒意義?) |
還有另外兩個不應該直接輸出的變量(是不應該,不是不能),一個是self.msg, 一個是self.args. 前者直接等於構造對象時傳入的參數msg,後者則對傳入的參數進行了點改造:如果是基本元素的tuple,直接賦值args;如果是dict的tuple,相應的dict賦值給args。 LogRecord類的getMessage函數主要就是整合這兩個變量,返回一個字符串。你可以構造Formatter字符串時使用%(message)s 輸出這個字符串。
可以使用str(LogRecord對象)查看具體的信息,具體輸出像這樣:
'<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,self.pathname, self.lineno, self.msg)
Formatter
其初始化函數爲 __init__(self, fmt=None, datefmt=None),其中fmt爲包含LogRecord中提到的“構造Formatter串對應項”的日誌格式串,例如,“%(name)s in the thread %(threadName)s”。其默認值爲”%s(message)\\n“。datefmt 爲一個時間格式串,具體的構造方式與構造time.strftime的時間格式串相同。對於日誌格式串除去上面提到的,還有一個: %(asctime)s。如果格式串中包含它的話,如果datefmt爲None,則%(asctime)s對應被替換爲time.strftime("%Y-%m-%d %H:%M:%S", time.localtime (record.created)) + “%03d” % record.msecs;否則,其被替換爲time.strftime(datefmt, time.localtime (record.created))。我這裏生成一個創建一個簡單的Formatter對象,以備後面使用。
expFormatter = Formatter ("%(levelname)s:%(name)s:%(message)s")
這個其實是後面提到的basicConfig( )所應用的一個Formatter。
Filter
這是一個Filter實現的示例性的類,其初始化函數爲:__init__(self, name='')。如果不傳入任何參數或者name = ‘.’,這個類的filter方法對任何LogRecord都返回True;如果傳入諸如“a.b”的name,則這個類的filter方會對任何name中包含‘a.b’的logger生成的LogRecord都返回true,其他的均返回Flase(這裏涉及到Logger的層次關係,後面細講)。
我們可以仿造(繼承也沒關係,不過我的感覺如果你的Filter和LogRecord名字無關,還是自己再實現一個把!)這個類實現你自己的Filter。構造自己的Filter類,必須要包含一個這樣的方法filter(self, record), 其中record爲一個LogRecord對象。返回值爲布爾類型,具體來說就是可以輸出該條日誌返回True,不可以輸出返回False。舉個很簡單的例子,實現一個只對id爲5的線程生成的LogRecord輸出True,其他的均爲False
class ThreadFilter(object):
def filter(self, record):
if record.thread == 5:
return True
returen False
Filterer
這是一個對所有Filter管理的類,而且初始化得到的對象是不包含任何Filter的,具體的成員函數爲:
addFilter(self, filter):添加一個filter對象
removeFilter(self, filter):刪除一個filter對象
filter(self, record):用所有其管理的filter處理當前日誌record
從繼承圖總我們也可以看到它是Logger和Handler的基類,在這兩個類中,都自動調用了filter成員函數,具體來說,都在它們的handle(self, record)中進行了調用。所以只需用addFilter添加和removeFilter刪除filter就可以啦,具體使用可以不必關心。舉個例子(用上面提到的幾個Filter類以及幾個沒有講解的類,但相信看了繼承圖,應該是沒有理解難度的):
nameF = Filter(“a.b”)
threadF = ThreadFilter( )
root = RootLogger(WARNING)
hdlr = FileHandler('file1','a')
root.addFilter(threadF)
hdlr.addFilter(nameF)
Handler
這個類主要實現日誌的輸出,其本身不可以直接使用,必須子類化它並實現一定的函數纔可以處理日誌,諸如在logging模塊中主要有實現了三個類型的Handler,分別是StreamHandler, FileHandler以及NullHandler
(1)幾個可能用到的成員函數:
__init__(self, level=NOTSET):初始化一個Handler只需要指定它的日誌級別,默認爲NOTSET
setLevel(self, level):設置可以處理的最低日誌等級
setFormatter(self, fmt):設置使用的Formatter,結合前面提到的一些東西,比如, hdlr.setFormatter(expFormatter), 這樣一來,前面例子中得hdlr就使用expFormatter這個Formatter 。若果不做任何設置,其默認使用的Formatter爲 Formatter( ), 也即日誌格式串爲 “%(message)s”,日誌中對應顯示的時間爲time.strftime("%Y-%m-%d %H:%M:%S", time.localtime (record.created)) + “%03d” % record.msecs的樣子
(2)其子類中實現的成員函數:
emit(self, record):這個函數必須在子類中實現,否則就會生成NotImplementedError的異常
flush(self):持久或日誌,這個在Handler類中啥也沒有實現,在子類中要自己設計實現
close(self):這個可以根據不同的handler重寫,但重寫時一定要調用父類的這個函數
(3)成員變量:
self.level :Handler的日誌級別,如果被處理的日誌的級別高於這個值,則處理;否則忽略
name :Handler的名字(這是用property加的一個變量)
StreamHandler
把日誌輸出到一個流中得Handler,初始化函數爲__init__(self, stream=None),如果不指定任何輸出流,輸出到sys.stderr中
FileHandler
把日誌文件輸出到一個文件中得Handler,上面曾經給出過一個實際應用的例子
NullHandler
這個Handler什麼也不做,一般把他的一個對象添加到一個logger上以避免這樣的信息:No handlers could be found for logger XXX
Logger
這個類主要用來生成LogRecord,並轉交給它自身的handler處理。
(1)成員變量
self.name :名字。由初始化函數給定
self.level:logger的日誌級別,默認有初始化函數輸入,不給定的話爲NOTSET。如果被處理的日誌的級別高於這個值,則處理;否則忽略
self.parent :這個logger的父logger,注意,這個類繼承沒有任何關係,下面的層次管理詳細道來!默認爲None
self.propagate :決定是否要讓它的父logger的handler來處理處理當前日誌。默認爲1,也即讓父logger來處理當前日誌
self.handlers :當前包含的的logger,默認爲空
self.disabled :決定是否可以處理日誌,默認爲0,也即要處理
manager:這個變量在logger層次管理中加入的,指向這個logger的管理器(整個log系統原則上來說只有一個manager)
(2)一些常用成員函數
setLevel(self, level):設定logger的日誌級別
debug(self, msg, *args, **kwargs):生成一個debug級別的日誌並處理,例如logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
info(self, msg, *args, **kwargs):生成一個info級別的日誌並處理
warning(self, msg, *args, **kwargs):生成一個warning級別的日誌並處理,warn(self, msg, *args, **kwargs)也實現相同功能
error(self, msg, *args, **kwargs):生成一個error級別的日誌並處理
exception(self, msg, *args):生成異常信息並處理,實際上它調用了error函數
critical(self, msg, *args, **kwargs):生成critical級別的日誌並處理,fatal(self, msg, *args, **kwargs)也實現相同功能
log(self, level, msg, *args, **kwargs):生成一個level級別的日誌並處理,例如logger.log(DEBUG, "We have a %s", "mysterious problem", exc_info=1)
addHandler(self, hdlr):給當前logger添加一個handler hdlr
removeHandler(self, hdlr):刪除掉當前logger中得一個handler hdlr
getChild(self, suffix):獲取當前logger名的一個子孫logger. 例如,當前logger名爲“ab”,suffix爲“c”, 它將返回一個名爲“ab.c”的logger(這和logger的層次架構有關係)
RootLogger
這是爲搭建logger的層次架構而繼承Logger而構造的一個類,它繼承字Logger類,初始化函數爲__init__(self, level),其中level爲制定的logger級別。實例化這樣的一個對象就會生成一個名爲root的logger。事實上,模塊自己幫我們實例化一個名爲“root”,級別爲warning的RootLogger。一方面用它來充當層次架構中得根logger;一方面模塊級別的函數都使用它處理日誌(比如ogging.debug()就使用的它)。
2. 相互的調用關係
爲了方便查看源代碼,這裏給出一個可能得相互調用關係圖(以Logger.critical爲例):
3. logging模塊級的函數和變量
(1)關於級別的定義,拷貝源碼過來:
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
這是log系統預定義的日誌級別以及其對應的數值。通過getLevelName(level)對應的字符串名字,也可以通過addLevelName(level, levelName)添加你自己的級別以及級別名。
(2)直接應用以及logger管理,拷貝源碼了:
root = RootLogger(WARNING): 在模塊定義了根logger
Logger.root = root
Logger.manager = Manager(Logger.root)):定義了一個logger的管理器,從這裏也可以看到root是作爲一個根logger的
再看看其他的一些函數:
getLogger(name=None):這個函數最常用,要不向manager註冊一個名爲name的Logger,要不返回root
BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s",爲下面basicConfig定義的日誌格式串
basicConfig(**kwargs):這個函數主要針對root做配置。如果root已經有handler,它不做任何動作。其默認行爲(沒有參數)是爲root創建並添加一個使用BASIC_FORMAT格式串StreamHandler,其流指向sys.stderr. 可以給定的參數包括:
filename 指定一個文件,去創建並添加一個 FileHandler
filemode 打開文件的方式,默認‘a‘
format Handler要使用的日誌格式串
datefmt 使用的時間格式串.
level 設置root的日記級別
stream 用這個流創建並添加一個StreamHandler。如果filename和它都指定,忽略它。
debug(self, msg, *args, **kwargs):使用root生成一個debug級別的日誌並處理,例如loggin.debug("Houston, we have a %s", "thorny problem", exc_info=1)
info(self, msg, *args, **kwargs):使用root生成一個info級別的日誌並處理
warning(self, msg, *args, **kwargs):使用root生成一個warning級別的日誌並處理,warn(self, msg, *args, **kwargs)也實現相同功能
error(self, msg, *args, **kwargs):使用root生成一個error級別的日誌並處理
exception(self, msg, *args):使用root生成異常信息並處理,實際上它調用了error函數
critical(self, msg, *args, **kwargs):使用root生成critical級別的日誌並處理,fatal(self, msg, *args, **kwargs)也實現相同功能
log(self, level, msg, *args, **kwargs):使用root生成一個level級別的日誌並處理,例如loggingr.log(DEBUG, "We have a %s", "mysterious problem", exc_info=1)
disable(level):通過manager通知所有的logger,低於level的所有日誌都不輸出
4. logger的層次架構
通過Manager類,loggin模塊實現了一個層次化管理的logger管理系統。比如,運行下面代碼:
import logging
logging.getLogger("a.b.c")
logging.getLogger("a.b.d")
logging.getLogger("a.b")
很自然,會生成三個Logger,名字分別爲“a.b.c”, “a.b.d” 和“a.b”,但同時還實現了一種樹一樣的層級關係。root 會有一個字Logger “a.b”, 而Logger “a.b”則會有兩個子Logger “a.b.c”和“a.b.d”。這樣做的好處就是:
1)可以有選擇的輸出模塊中子模塊。以實現soap客戶端的suds爲例,在實際應用中可以很簡單的使用如下語句指定輸出 suds.client debug級別以上的日誌,而其他模塊,比如suds.transport 則不會輸出任何日誌信息。
import logging
logging.basicConfig( )
logging.getLogger('suds.client').setLevel(logging.DEBUG)
2)方便配置Handler。依賴層級之間的回調關係,可以不必爲每個Logger指定Handler,只要其父Logger有指定的Handler就可以。最簡單的做法是指指定根 Logger的Handler就可以,上例中使用的就是這種辦法
5. Logger的配置及使用
基本的配置可以學學basicConfig函數,拷貝源碼了
_acquireLock()
try:
if len(root.handlers) == 0:
filename = kwargs.get("filename")
if filename:
mode = kwargs.get("filemode", 'a')
hdlr = FileHandler(filename, mode)
else:
stream = kwargs.get("stream")
hdlr = StreamHandler(stream)
fs = kwargs.get("format", BASIC_FORMAT)
dfs = kwargs.get("datefmt", None)
fmt = Formatter(fs, dfs)
hdlr.setFormatter(fmt)
root.addHandler(hdlr)
level = kwargs.get("level")
if level is not None:
root.setLevel(level)
finally:
_releaseLock()
還有我從別的被人拷貝的[4]
import logging
#create logger
logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)
#create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
#create formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
#add formatter to ch
ch.setFormatter(formatter)
#add ch to logger
logger.addHandler(ch)
#"application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")
另外, logging.config模塊可以支持從配置讀入,相同的實現第二個例子【4】:
配置文件:
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
程序如下:
import logging
import logging.config
logging.config.fileConfig("logging.conf")
#create logger
logger = logging.getLogger("simpleExample")
#"application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")
6. logging.handlers模塊提供的其他幾個類型的handler【2】
BaseRotatingHandler是所有輪徇日誌的基類,不能直接使用。但是可以使用RotatingFileHandler和TimeRotatingFileHandler。RotatingFileHandler實例發送信息到磁盤文件,並且限制最大的日誌文件大小,並適時輪徇。 TimeRotatingFileHandler實例發送錯誤信息到磁盤,並在適當的事件間隔進行輪徇。 SocketHandler實例發送日誌到TCP/IP socket。 DatagramHandler實例發送錯誤信息通過UDP協議。 SMTPHandler實例發送錯誤信息到特定的email地址。 SysLogHandler實例發送日誌到UNIX syslog服務,並支持遠程syslog服務。 NTEventLogHandler實例發送日誌到WindowsNT/2000/XP事件日誌。 MemoryHandler實例發送日誌到內存中的緩衝區,並在達到特定條件時清空。 HTTPHandler實例發送錯誤信息到HTTP服務器,通過GET或POST方法。
參考文獻:
【1】http://www.python.org/dev/peps/pep-0282/
【2】http://www.cnblogs.com/sislcb/archive/2008/11/25/1340627.html
【3】http://www.cnblogs.com/SophiaTang/archive/2011/09/19/2181471.html