1. 程式人生 > >Python中的logging模組就這麼用

Python中的logging模組就這麼用

1、日誌級別

日誌一共分成5個等級,從低到高分別是:DEBUG INFO WARNING ERROR CRITICAL。

DEBUG:詳細的資訊,通常只出現在診斷問題上
INFO:確認一切按預期執行
WARNING:一個跡象表明,一些意想不到的事情發生了,或表明一些問題在不久的將來(例如。磁碟空間低”)。這個軟體還能按預期工作。
ERROR:更嚴重的問題,軟體沒能執行一些功能
CRITICAL:一個嚴重的錯誤,這表明程式本身可能無法繼續執行

這5個等級,也分別對應5種打日誌的方法: debug 、info 、warning 、error 、critical。預設的是WARNING,當在WARNING或之上時才被跟蹤。

2、日誌輸出

有兩種方式記錄跟蹤,一種輸出控制檯,另一種是記錄到檔案中,如日誌檔案。

2.1、將日誌輸出到控制檯

比如,編寫一個叫做log.py的檔案,如下:

# coding=utf-8
__author__ = 'liu.chunming'
import logging

logging.basicConfig(level=logging.WARNING,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# use logging
logging.info('this is a loggging info message')
logging.debug('this is a loggging debug message')
logging.warning('this is loggging a warning message')
logging.error('this is an loggging error message')
logging.critical('this is a loggging critical message')
執行上面的程式碼將在Console中輸出下面資訊:

C:\Python27\python.exe C:/Users/liu.chunming/PycharmProjects/Myproject/log.py
2015-05-21 17:25:22,572 - log.py[line:10] - WARNING: this is loggging a warning message
2015-05-21 17:25:22,572 - log.py[line:11] - ERROR: this is an loggging error message
2015-05-21 17:25:22,572 - log.py[line:12] - CRITICAL: this is a loggging critical message

【解析】

通過logging.basicConfig函式對日誌的輸出格式及方式做相關配置,上面程式碼設定日誌的輸出等級是WARNING級別,意思是WARNING級別以上的日誌才會輸出。另外還制定了日誌輸出的格式。

2.2、將日誌輸出到檔案

我們還可以將日誌輸出到檔案,只需要在logging.basicConfig函式中設定好輸出檔案的檔名和寫檔案的模式。

# coding=utf-8
__author__ = 'liu.chunming'
import logging

logging.basicConfig(level=logging.WARNING,
                    filename='./log/log.txt',
                    filemode='w',
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# use logging
logging.info('this is a loggging info message')
logging.debug('this is a loggging debug message')
logging.warning('this is loggging a warning message')
logging.error('this is an loggging error message')
logging.critical('this is a loggging critical message')
執行之後,開啟該檔案./log/log.txt,效果如下:

2015-05-21 17:30:20,282 - log.py[line:12] - WARNING: this is loggging a warning message
2015-05-21 17:30:20,282 - log.py[line:13] - ERROR: this is an loggging error message
2015-05-21 17:30:20,282 - log.py[line:14] - CRITICAL: this is a loggging critical message

2.3、既要把日誌輸出到控制檯, 還要寫入日誌檔案

這就需要一個叫作Logger 的物件來幫忙,下面將對他進行詳細介紹,現在這裡先學習怎麼實現把日誌既要輸出到控制檯又要輸出到檔案的功能。

# coding=utf-8
__author__ = 'liu.chunming'
import logging

# 第一步,建立一個logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)    # Log等級總開關

# 第二步,建立一個handler,用於寫入日誌檔案
logfile = './log/logger.txt'
fh = logging.FileHandler(logfile, mode='w')
fh.setLevel(logging.DEBUG)   # 輸出到file的log等級的開關

# 第三步,再建立一個handler,用於輸出到控制檯
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING)   # 輸出到console的log等級的開關

# 第四步,定義handler的輸出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 第五步,將logger新增到handler裡面
logger.addHandler(fh)
logger.addHandler(ch)

# 日誌
logger.debug('this is a logger debug message')
logger.info('this is a logger info message')
logger.warning('this is a logger warning message')
logger.error('this is a logger error message')
logger.critical('this is a logger critical message')
執行這段程式碼之後,在console中,可以看到:

C:\Python27\python.exe C:/Users/liu.chunming/PycharmProjects/Myproject/log.py
2015-05-21 17:47:50,292 - log.py[line:30] - WARNING: this is a logger warning message
2015-05-21 17:47:50,292 - log.py[line:31] - ERROR: this is a logger error message
2015-05-21 17:47:50,293 - log.py[line:32] - CRITICAL: this is a logger critical message
在logger.txt中,可以看到:

2015-05-21 17:47:50,292 - log.py[line:29] - INFO: this is a logger info message
2015-05-21 17:47:50,292 - log.py[line:30] - WARNING: this is a logger warning message
2015-05-21 17:47:50,292 - log.py[line:31] - ERROR: this is a logger error message
2015-05-21 17:47:50,293 - log.py[line:32] - CRITICAL: this is a logger critical message

【解析】

可以發現,實現這個功能一共分5步:

第一步,建立一個logger;第二步,建立一個handler,用於寫入日誌檔案;第三步,再建立一個handler,用於輸出到控制檯;第四步,定義handler的輸出格式;第五步,將logger新增到handler裡面。這段程式碼裡面提到了好多概念,包括:Logger,Handler,Formatter。後面講對這些概念進行講解。

3、多個模組中日誌輸出順序

通常我們的工作中會有多個模組都需要輸出日誌。那麼,具有呼叫關係的模組之間,它門的日誌輸出順序是怎麼樣的?我們來演示下:假設有兩個檔案,分別是util.py:
# util.py
__author__ = 'liu.chunming'
import logging

def fun():
    logging.info('this is a log in util module')
和main.py
# main.py
# coding=utf-8
__author__ = 'liu.chunming'
import logging
import util

logging.basicConfig(level=logging.INFO,
                    filename='./log/log.txt',
                    filemode='w',
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
def main():
    logging.info('main module start')
    util.fun()
    logging.info('main module stop')

if __name__ == '__main__':
    main()
執行後開啟log.txt,結果如下:
2015-05-21 18:10:34,684 - main.py[line:11] - INFO: main module start
2015-05-21 18:10:34,684 - util.py[line:5] - INFO: this is a log in util module
2015-05-21 18:10:34,684 - main.py[line:13] - INFO: main module stop
【解析】
可以看出,日誌的輸出順序就是模組的執行順序。

4、日誌格式說明

logging.basicConfig函式中,可以指定日誌的輸出格式format,這個引數可以輸出很多有用的資訊,如上例所示:

%(levelno)s: 列印日誌級別的數值
%(levelname)s: 列印日誌級別名稱
%(pathname)s: 列印當前執行程式的路徑,其實就是sys.argv[0]
%(filename)s: 列印當前執行程式名
%(funcName)s: 列印日誌的當前函式
%(lineno)d: 列印日誌的當前行號
%(asctime)s: 列印日誌的時間
%(thread)d: 列印執行緒ID
%(threadName)s: 列印執行緒名稱
%(process)d: 列印程序ID
%(message)s: 列印日誌資訊

我在工作中給的常用格式在前面已經看到了。就是:

format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'

這個格式可以輸出日誌的列印時間,是哪個模組輸出的,輸出的日誌級別是什麼,以及輸入的日誌內容。

5、高階進階

接下來學習一些日誌元件以及一些高階部分。日誌元件包括:loggers、handlers,filters,formatters.

Logger 物件扮演了三重角色.首先,它暴露給應用幾個方法以便應用可以在執行時寫log.其次,Logger物件按照log資訊的嚴重程度或者根據filter對 象來決定如何處理log資訊(預設的過濾功能).最後,logger還負責把log資訊傳送給相關的loghandlers.

Handler物件負責分配合適的log資訊(基於log資訊的嚴重 程度)到handler指定的目的地.Logger物件可以用addHandler()方法新增零個或多個handler物件到它自身.一個常見的場景 是,一個應用可能希望把所有的log資訊都發送到一個log檔案中去,所有的error級別以上的log資訊都發送到stdout,所有critical 的log資訊通過email傳送.這個場景裡要求三個不同handler處理,每個handler負責把特定的log資訊傳送到特定的地方.

filter:細緻化,選擇哪些日誌輸出

format:設定顯示格式

1、logging.basicConfig([**kwargs]):

Does basic configuration for the logging system by creating a StreamHandler with a defaultFormatter and adding it to the root logger. The functionsdebug(),info(),warning(),error() andcritical() will callbasicConfig() automatically if no handlers are defined for the root logger.

This function does nothing if the root logger already has handlers configured for it.

為日誌模組配置基本資訊。kwargs 支援如下幾個關鍵字引數:
filename :日誌檔案的儲存路徑。如果配置了些引數,將自動建立一個FileHandler作為Handler;
filemode
:日誌檔案的開啟模式。 預設值為'a',表示日誌訊息以追加的形式新增到日誌檔案中。如果設為'w', 那麼每次程式啟動的時候都會建立一個新的日誌檔案;
format :設定日誌輸出格式;
datefmt :定義日期格式;
level :設定日誌的級別.對低於該級別的日誌訊息將被忽略;
stream :設定特定的流用於初始化StreamHandler;

演示如下:

複製程式碼
import logging
import os
FILE=os.getcwd()

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s:%(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename = os.path.join(FILE,'log.txt'),
                    filemode='w')
logging.info('msg')
logging.debug('msg2')
複製程式碼

2、logging.getLogger([name])

建立Logger物件。日誌記錄的工作主要由Logger物件來完成。在呼叫getLogger時要提供Logger的名稱(注:多次使用相同名稱 來呼叫getLogger,返回的是同一個物件的引用。),Logger例項之間有層次關係,這些關係通過Logger名稱來體現,如:

p = logging.getLogger("root")

c1 = logging.getLogger("root.c1")

c2 = logging.getLogger("root.c2")

例子中,p是父logger, c1,c2分別是p的子logger。c1, c2將繼承p的設定。如果省略了name引數, getLogger將返回日誌物件層次關係中的根Logger。

複製程式碼
import logging
'''命名'''
log2=logging.getLogger('BeginMan')  #生成一個日誌物件
print log2  #<logging.Logger object at 0x00000000026D1710>

'''無名'''
log3 = logging.getLogger()
print log3  #<logging.RootLogger object at 0x0000000002721630> 如果沒有指定name,則返回RootLogger

'''最好的方式'''
log = logging.getLogger(__name__)#__name__ is the module’s name in the Python package namespace.
print log   #<logging.Logger object at 0x0000000001CD5518>  Logger物件
print __name__  #__main__
複製程式碼

三、Logger物件

通過logging.getLogger(nam)來獲取Logger物件,

Class logging.Logger

有如下屬性和方法:

1、Logger.propagate

print log.propagate         #1

具體參考:http://docs.python.org/2.7/library/logging.html

2、Logger.setLevel(lvl)

設定日誌的級別。對於低於該級別的日誌訊息將被忽略.

複製程式碼
import logging
import os
logging.basicConfig(format="%(levelname)s,%(message)s",filename=os.path.join(os.getcwd(),'log.txt'),level=logging.DEBUG)
log = logging.getLogger('root.set')   #Logger物件
print log.propagate         #1
log.setLevel(logging.WARN)  #日誌記錄級別為WARNNING  
log.info('msg')             #不會被記錄
log.debug('msg')            #不會被記錄
log.warning('msg')
log.error('msg')
複製程式碼

3、Logger.debug(msg [ ,*args [, **kwargs]])

記錄DEBUG級別的日誌資訊。引數msg是資訊的格式,args與kwargs分別是格式引數。

複製程式碼
import logging
logging.basicConfig(filename = os.path.join(os.getcwd(), 'log.txt'), level = logging.DEBUG)
log = logging.getLogger('root')
log.debug('%s, %s, %s', *('error', 'debug', 'info'))
log.debug('%(module)s, %(info)s', {'module': 'log', 'info': 'error'})
複製程式碼

4、同上

Logger.info(msg[ , *args[ , **kwargs] ] )

Logger.warnning(msg[ , *args[ , **kwargs] ] )

Logger.error(msg[ , *args[ , **kwargs] ] )

Logger.critical(msg[ , *args[ , **kwargs] ] )

5、Logger.log(lvl, msg[ , *args[ , **kwargs]] )

記錄日誌,引數lvl使用者設定日誌資訊的級別。引數msg, *args, **kwargs的含義與Logger.debug一樣。

log.log(logging.ERROR,'%(module)s %(info)s',{'module':'log日誌','info':'error'}) #ERROR,log日誌 error
log.log(logging.ERROR,'再來一遍:%s,%s',*('log日誌','error'))  #ERROR,再來一遍:log日誌,error

6、Logger.exception(msg[, *args])

以ERROR級別記錄日誌訊息,異常跟蹤資訊將被自動新增到日誌訊息裡。Logger.exception通過用在異常處理塊中,如:

複製程式碼
import logging
import os
logging.basicConfig(format="%(levelname)s,%(message)s",filename=os.path.join(os.getcwd(),'log.txt'),level=logging.DEBUG)
log = logging.getLogger('root')   #Logger物件
try:
    raise Exception,u'錯誤異常'
except:
    log.exception('exception')  #異常資訊被自動新增到日誌訊息中  
開啟檔案,顯示如下:

'''ERROR,exception
Traceback (most recent call last):
  File "E:\project\py\src\log3.py", line 12, in <module>
    raise Exception,u'錯誤異常'
Exception: 錯誤異常
'''
複製程式碼

7、Logger.addFilter(filt)

指定過濾器

8、Logger.removeFilter(filt)

移除指定的過濾器

9、Logger.filter(record)

....其他的後面介紹

四、 Handler物件、Formatter物件、Filter物件、Filter物件

這裡簡要介紹

複製程式碼
#coding=utf8
'''
Created on 2013年9月23日
Function : Handler物件、Formatter物件、Filter物件、Filter物件
@author : BeginMan
'''
import logging
import os
'''Logger'''
l = logging.Logger('root')          #建立Logger物件
log = logging.getLogger('root')     #通過logging.getLogger建立Logger物件
print l                             #<logging.Logger object at 0x0000000001DF5B70>
print log                           #<logging.Logger object at 0x00000000022A16D8>

'''Handler'''
handler = logging.Handler()         #建立Handler物件
handler.__init__(logging.DEBUG)     #通過設定level來初始化Handler例項
handler.createLock()                #初始化一個執行緒鎖可以用來序列化訪問底層I / O功能,這可能不是執行緒安全的。
handler.acquire()                   #獲取執行緒鎖通過handler.createLock()
handler.release()                   #釋放執行緒鎖通過獲取handler.acquire()
handler.setLevel(logging.DEBUG)     #設定臨界值,如果Logging資訊級別小於它則被忽視,當一個handler物件被建立,級別沒有被設定,導致所有的資訊會被處理。
handler.setFormatter("%(levelname)s,%(message)s")              #設定格式
# handler.addFilter(filter)         #新增指定的過濾器
# handler.removeFilter(filter)      #移除指定的過濾器
# handler.filter(record)            #通過設定過濾器適用於記錄並返回真值如果要處理的記錄
handler.flush()                     #確保所有的日誌輸出已經被重新整理
handler.close()                     #收拾任何所使用資源處理程式,
# handler.handle(record)            #有條件地發出指定的日誌記錄,這取決於過濾器可能被新增到處理程式。
# handler.handlerError(record)      #處理錯誤
# handler.format(record)            #格式輸出
# handler.emit(record)

#Formatter:http://docs.python.org/2.7/library/logging.html#logging.Formatter

'''Formatter:
the base Formatter allows a formatter string to be specified,is none ,used default value '%(message)s'
class logging.Formatter(fmt=None,datefmt=None)
If no fmt is specified, '%(message)s' is used. If no datefmt is specified, the ISO8601 date format is used.
'''
fm = logging.Formatter('%(levelname)s:%(message)s','%m/%d/%Y %I:%M:%S %p')
print fm        #<logging.Formatter object at 0x0000000002311828>
#有如下方法:format()、formatTime()、formatException()

#http://docs.python.org/2.7/library/logging.html#formatter-objects

'''Filter'''
'''
class logging.Filter(name=''):
    返回Filter例項,If name is specified, it names a logger which, together with its children, 
  will have its events allowed through the filter. If name is the empty string, allows every event.
用於Loggers、Handlers等過濾的設定
如果一個過濾器初始化為'A.B',則允許'A.B/A.B.C/A.B.C.D/A.B.D'等,但不允許'A.BB/B.A'等通過
如果初始化為空字串,則沒有過濾。
它有filter()方法
'''

'''LogRecord '''
'''class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None)
LogRecord 例項可以被Logger自動建立.也可以通過makeLogRecord()來建立'''
#http://docs.python.org/2.7/library/logging.html#logrecord-objects