目錄 | 上一節 (8.1 測試) | [下一節 (8.3 調試)]()python
本節對日誌模塊(logging module)進行簡單的介紹。git
logging
模塊是用於記錄診斷信息的 Python 標準庫模塊。日誌模塊很是龐大,具備許多複雜的功能。咱們將會展現一個簡單的例子來講明其用處。github
在本節練習中,咱們建立這樣一個 parse()
函數:segmentfault
# fileparse.py def parse(f, types=None, names=None, delimiter=None): records = [] for line in f: line = line.strip() if not line: continue try: records.append(split(line,types,names,delimiter)) except ValueError as e: print("Couldn't parse :", line) print("Reason :", e) return records
請看到 try-except
語句,在 except
塊中,咱們應該作什麼?app
應該打印警告消息(warning message)?函數
try: records.append(split(line,types,names,delimiter)) except ValueError as e: print("Couldn't parse :", line) print("Reason :", e)
仍是默默忽略警告消息?測試
try: records.append(split(line,types,names,delimiter)) except ValueError as e: pass
任何一種方式都沒法使人滿意,一般狀況下,兩種方式咱們都須要(用戶可選)。ui
logging
模塊能夠解決這個問題:命令行
# fileparse.py import logging log = logging.getLogger(__name__) def parse(f,types=None,names=None,delimiter=None): ... try: records.append(split(line,types,names,delimiter)) except ValueError as e: log.warning("Couldn't parse : %s", line) log.debug("Reason : %s", e)
修改代碼以使程序可以遇到問題的時候發出警告消息,或者特殊的 Logger
對象。 Logger
對象使用 logging.getLogger(__name__)
建立。翻譯
建立一個記錄器對象(logger object)。
log = logging.getLogger(name) # name is a string
發出日誌消息:
log.critical(message [, args]) log.error(message [, args]) log.warning(message [, args]) log.info(message [, args]) log.debug(message [, args])
不一樣方法表明不一樣級別的嚴重性。
全部的方法都建立格式化的日誌消息。args
和 %
運算符 一塊兒使用以建立消息。
logmsg = message % args # Written to the log
配置:
# main.py ... if __name__ == '__main__': import logging logging.basicConfig( filename = 'app.log', # Log output file level = logging.INFO, # Output level )
一般,在程序啓動時,日誌配置是一次性的(譯註:程序啓動後沒法從新配置)。該配置與日誌調用是分開的。
日誌是能夠任意配置的。你能夠對日誌配置的任何一方面進行調整:如輸出文件,級別,消息格式等等,沒必要擔憂對使用日誌模塊的代碼形成影響。
在 fileparse.py
中,有一些與異常有關的錯誤處理,這些異常是由錯誤輸入引發的。以下所示:
# fileparse.py import csv def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): ''' Parse a CSV file into a list of records with type conversion. ''' if select and not has_headers: raise RuntimeError('select requires column headers') rows = csv.reader(lines, delimiter=delimiter) # Read the file headers (if any) headers = next(rows) if has_headers else [] # If specific columns have been selected, make indices for filtering and set output columns if select: indices = [ headers.index(colname) for colname in select ] headers = select records = [] for rowno, row in enumerate(rows, 1): if not row: # Skip rows with no data continue # If specific column indices are selected, pick them out if select: row = [ row[index] for index in indices] # Apply type conversion to the row if types: try: row = [func(val) for func, val in zip(types, row)] except ValueError as e: if not silence_errors: print(f"Row {rowno}: Couldn't convert {row}") print(f"Row {rowno}: Reason {e}") continue # Make a dictionary or a tuple if headers: record = dict(zip(headers, row)) else: record = tuple(row) records.append(record) return records
請注意發出診斷消息的 print
語句。使用日誌操做來替換這些 print
語句相對來講更簡單。請像下面這樣修改代碼:
# fileparse.py import csv import logging log = logging.getLogger(__name__) def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): ''' Parse a CSV file into a list of records with type conversion. ''' if select and not has_headers: raise RuntimeError('select requires column headers') rows = csv.reader(lines, delimiter=delimiter) # Read the file headers (if any) headers = next(rows) if has_headers else [] # If specific columns have been selected, make indices for filtering and set output columns if select: indices = [ headers.index(colname) for colname in select ] headers = select records = [] for rowno, row in enumerate(rows, 1): if not row: # Skip rows with no data continue # If specific column indices are selected, pick them out if select: row = [ row[index] for index in indices] # Apply type conversion to the row if types: try: row = [func(val) for func, val in zip(types, row)] except ValueError as e: if not silence_errors: log.warning("Row %d: Couldn't convert %s", rowno, row) log.debug("Row %d: Reason %s", rowno, e) continue # Make a dictionary or a tuple if headers: record = dict(zip(headers, row)) else: record = tuple(row) records.append(record) return records
完成修改後,嘗試在錯誤的數據上使用這些代碼:
>>> import report >>> a = report.read_portfolio('Data/missing.csv') Row 4: Bad row: ['MSFT', '', '51.23'] Row 7: Bad row: ['IBM', '', '70.44'] >>>
若是你什麼都不作,則只會得到 WARNING
級別以上的日誌消息。輸出看起來像簡單的打印語句。可是,若是你配置了日誌模塊,你將會得到有關日誌級別,模塊等其它信息。請按如下步驟操做查看:
>>> import logging >>> logging.basicConfig() >>> a = report.read_portfolio('Data/missing.csv') WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23'] WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44'] >>>
你會發現,看不到來自於 log.debug()
操做的輸出。請按如下步驟修改日誌級別(譯註:由於日誌配置是一次性的,因此該操做須要重啓命令行窗口):
>>> logging.getLogger('fileparse').level = logging.DEBUG >>> a = report.read_portfolio('Data/missing.csv') WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23'] DEBUG:fileparse:Row 4: Reason: invalid literal for int() with base 10: '' WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44'] DEBUG:fileparse:Row 7: Reason: invalid literal for int() with base 10: '' >>>
只留下 critical 級別的日誌消息,關閉其它級別的日誌消息。
>>> logging.getLogger('fileparse').level=logging.CRITICAL >>> a = report.read_portfolio('Data/missing.csv') >>>
要添加日誌到應用中,你須要某種機制來實如今主模塊中初始化日誌。其中一種方式使用看起來像下面這樣的代碼:
# This file sets up basic configuration of the logging module. # Change settings here to adjust logging output as needed. import logging logging.basicConfig( filename = 'app.log', # Name of the log file (omit to use stderr) filemode = 'w', # File mode (use 'a' to append) level = logging.WARNING, # Logging level (DEBUG, INFO, WARNING, ERROR, or CRITICAL) )
再次說明,你須要將日誌配置代碼放到程序啓動步驟中。例如,將其放到 report.py
程序裏的什麼位置?
目錄 | 上一節 (8.1 測試) | [下一節 (8.3 調試)]()