學一門新技術或者新語言,我們都要首先學會如何去適應這們新技術,其中在適應過程中,我們必須得學習如何偵錯工具並打出相應的log資訊來,正所謂“只要log打的好,沒有bug解不了”,在我們熟知的一些資訊技術中,log4xxx系列以及開發Android app時的android.util.Log包等等都是為了開發人員更好的得到log資訊服務的。在Python這門語言中,我們同樣可以根據自己的程式需要打出log。
log資訊不同於使用打樁法列印一定的標記資訊,log可以根據程式需要而分出不同的log層級,比如info、debug、warn等等層級的資訊,只要即時控制log層級開關就可以為開發人員提供更好的log資訊,與log4xx類似,logger,handler和日誌訊息的調用可以有具體的記錄層級(Level),只有在日誌訊息的層級大於logger和handler的設定的層級,才會顯示。下面我就來談談我在Python中使用的logging模組一些方法。
logging模組介紹
Python的logging模組提供了通用的日誌系統,熟練使用logging模組可以方便開發人員開發第三方模組或者是自己的Python應用。同樣這個模組提供不同的記錄層級,並可以採用不同的方式記錄日誌,比如檔案,HTTP、GET/POST,SMTP,Socket等,甚至可以自己實現具體的日誌記錄方式。下文我將主要介紹如何使用檔案方式記錄log。
logging模組包括logger,handler,filter,formatter這四個基本概念。
logger:提供日誌介面,供應用代碼使用。logger最長用的操作有兩類:配置和發送日誌訊息。可以通過logging.getLogger(name)擷取logger對象,如果不指定name則返回root對象,多次使用相同的name調用getLogger方法返回同一個logger對象。
handler:將日誌記錄(log record)發送到合適的目的地(destination),比如檔案,socket等。一個logger對象可以通過addHandler方法添加0到多個handler,每個handler又可以定義不同記錄層級,以實現日誌分級過濾顯示。
filter:提供一種優雅的方式決定一個日誌記錄是否發送到handler。
formatter:指定日誌記錄輸出的具體格式。formatter的構造方法需要兩個參數:訊息的格式字串和日期文字,這兩個參數都是可選的。
基本使用方法
一些小型的程式我們不需要構造太複雜的log系統,可以直接使用logging模組的basicConfig函數即可,代碼如下:
複製代碼 代碼如下:
'''
Created on 2012-8-12
@author: walfred
@module: loggingmodule.BasicLogger
'''
import logging
log_file = "./basic_logger.log"
logging.basicConfig(filename = log_file, level = logging.DEBUG)
logging.debug("this is a debugmsg!")
logging.info("this is a infomsg!")
logging.warn("this is a warn msg!")
logging.error("this is a error msg!")
logging.critical("this is a critical msg!")
運行程式時我們就會在該檔案的目前的目錄下發現basic_logger.log檔案,查看basic_logger.log內容如下:
複製代碼 代碼如下:
INFO:root:this is a info msg!
DEBUG:root:this is a debug msg!
WARNING:root:this is a warn msg!
ERROR:root:this is a error msg!
CRITICAL:root:this is a critical msg!
需要說明的是我將level設定為DEBUG層級,所以log日誌中只顯示了包含該層級及該層級以上的log資訊。資訊層級依次是:notset、debug、info、warn、error、critical。如果在多個模組中使用這個配置的話,只需在主模組中配置即可,其他模組會有相同的使用效果。
較進階版本
上述的基礎使用比較簡單,沒有顯示出logging模組的厲害,適合小程式用,現在我介紹一個較進階版本的代碼,我們需要依次設定logger、handler、formatter等配置。
複製代碼 代碼如下:
'''
Created on 2012-8-12
@author: walfred
@module: loggingmodule.NomalLogger
'''
import logging
log_file = "./nomal_logger.log"
log_level = logging.DEBUG
logger = logging.getLogger("loggingmodule.NomalLogger")
handler = logging.FileHandler(log_file)
formatter = logging.Formatter("[%(levelname)s][%(funcName)s][%(asctime)s]%(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(log_level)
#test
logger.debug("this is a debug msg!")
logger.info("this is a info msg!")
logger.warn("this is a warn msg!")
logger.error("this is a error msg!")
logger.critical("this is a critical msg!")
這時我們查看目前的目錄的nomal_logger.log記錄檔,如下:
複製代碼 代碼如下:
[DEBUG][][2012-08-12 17:43:59,295]this is a debug msg!
[INFO][][2012-08-12 17:43:59,295]this is a info msg!
[WARNING][][2012-08-12 17:43:59,295]this is a warn msg!
[ERROR][][2012-08-12 17:43:59,295]this is a error msg!
[CRITICAL][][2012-08-12 17:43:59,295]this is a critical msg!
這個對照前面介紹的logging模組,不難理解,下面的最終版本將會更加完整。
完善版本
這個最終版本我用singleton設計模式來寫一個Logger類,代碼如下:
複製代碼 代碼如下:
'''
Created on 2012-8-12
@author: walfred
@module: loggingmodule.FinalLogger
'''
import logging.handlers
class FinalLogger:
logger = None
levels = {"n" : logging.NOTSET,
"d" : logging.DEBUG,
"i" : logging.INFO,
"w" : logging.WARN,
"e" : logging.ERROR,
"c" : logging.CRITICAL}
log_level = "d"
log_file = "final_logger.log"
log_max_byte = 10 * 1024 * 1024;
log_backup_count = 5
@staticmethod
def getLogger():
if FinalLogger.logger is not None:
return FinalLogger.logger
FinalLogger.logger = logging.Logger("oggingmodule.FinalLogger")
log_handler = logging.handlers.RotatingFileHandler(filename = FinalLogger.log_file,\
maxBytes = FinalLogger.log_max_byte,\
backupCount = FinalLogger.log_backup_count)
log_fmt = logging.Formatter("[%(levelname)s][%(funcName)s][%(asctime)s]%(message)s")
log_handler.setFormatter(log_fmt)
FinalLogger.logger.addHandler(log_handler)
FinalLogger.logger.setLevel(FinalLogger.levels.get(FinalLogger.log_level))
return FinalLogger.logger
if __name__ == "__main__":
logger = FinalLogger.getLogger()
logger.debug("this is a debug msg!")
logger.info("this is a info msg!")
logger.warn("this is a warn msg!")
logger.error("this is a error msg!")
logger.critical("this is a critical msg!")
目前的目錄下的 final_logger.log內容如下:
複製代碼 代碼如下:
[DEBUG][][2012-08-12 18:12:23,029]this is a debug msg!
[INFO][][2012-08-12 18:12:23,029]this is a info msg!
[WARNING][][2012-08-12 18:12:23,029]this is a warn msg!
[ERROR][][2012-08-12 18:12:23,029]this is a error msg!
[CRITICAL][][2012-08-12 18:12:23,029]this is a critical msg!
這個final版本,也是我一直用的,讀者朋友也可以再加上其他的一些Handler,比如StreamHandler等等來擷取更多的log資訊,當然也可以將你的log資訊通過設定檔來完成。