日志記錄的重要性
- 日志記錄更容易排查到問題的所在之處
- 節(jié)省時間
日志記錄的流程框架
從下圖中我們可以看出看到這幾種 Python 類型途样,Logger、LogRecord佛寿、Filter幌墓、Handler、Formatter冀泻。
類型說明:
Logger:日志常侣,暴露函數(shù)給應(yīng)用程序,基于日志記錄器和過濾器級別決定哪些日志有效弹渔。
LogRecord :日志記錄器胳施,將日志傳到相應(yīng)的處理器處理。
Handler :處理器, 將(日志記錄器產(chǎn)生的)日志記錄發(fā)送至合適的目的地肢专。
Filter :過濾器, 提供了更好的粒度控制,它可以決定輸出哪些日志記錄舞肆。
Formatter:格式化器, 指明了最終輸出中日志記錄的布局焦辅。
- 判斷 Logger 對象對于設(shè)置的級別是否可用,如果可用椿胯,則往下執(zhí)行筷登,否則,流程結(jié)束哩盲。
- 創(chuàng)建 LogRecord 對象前方,如果注冊到 Logger 對象中的 Filter 對象過濾后返回 False,則不記錄日志廉油,流程結(jié)束惠险,否則,則向下執(zhí)行抒线。
- LogRecord 對象將 Handler 對象傳入當(dāng)前的 Logger 對象班巩,(圖中的子流程)如果 Handler 對象的日志級別大于設(shè)置的日志級別,再判斷注冊到 Handler 對象中的 Filter 對象過濾后是否返回 True 而放行輸出日志信息嘶炭,否則不放行趣竣,流程結(jié)束。
- 如果傳入的 Handler 大于 Logger 中設(shè)置的級別旱物,也即 Handler 有效遥缕,則往下執(zhí)行,否則宵呛,流程結(jié)束单匣。
- 判斷這個 Logger 對象是否還有父 Logger 對象,如果沒有(代表當(dāng)前 Logger 對象是最頂層的 Logger 對象 root Logger)宝穗,流程結(jié)束户秤。否則將 Logger 對象設(shè)置為它的父 Logger 對象,重復(fù)上面的 3逮矛、4 兩步鸡号,輸出父類 Logger 對象中的日志輸出,直到是 root Logger 為止须鼎。
日志輸出格式
- 日志的輸出格式可以認(rèn)為設(shè)置鲸伴,默認(rèn)格式為下圖所示。
日志基本使用
logging 使用非常簡單晋控,使用 basicConfig() 方法就能滿足基本的使用需要汞窗,如果方法沒有傳入?yún)?shù),會根據(jù)默認(rèn)的配置創(chuàng)建Logger 對象赡译,默認(rèn)的日志級別被設(shè)置為 WARNING仲吏,默認(rèn)的日志輸出格式如上圖,該函數(shù)可選的參數(shù)如下表所示。
參數(shù)名稱 | 參數(shù)描述 |
---|---|
filename | 日志輸出到文件的文件名 |
filemode | 文件模式裹唆,r[+]誓斥、w[+]、a[+] |
format | 日志輸出的格式 |
datefat | 日志附帶日期時間的格式 |
style | 格式占位符许帐,默認(rèn)為 "%" 和 “{}” |
level | 設(shè)置日志輸出級別 |
stream | 定義輸出流岖食,用來初始化 StreamHandler 對象,不能 filename 參數(shù)一起使用卷哩,否則會ValueError 異常 |
handles | 定義處理器把将,用來創(chuàng)建 Handler 對象,不能和 filename 、stream 參數(shù)一起使用吃谣,否則也會拋出 ValueError 異常 |
示例代碼如下:
import logging
logging.basicConfig()
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
傳入常用的參數(shù),示例代碼如下(這里日志格式占位符中的變量放到后面介紹):
import logging
logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
生成的日志文件 test.log 烙博,內(nèi)容如下:
13-10-18 21:10:32 root:DEBUG:This is a debug message
13-10-18 21:10:32 root:INFO:This is an info message
13-10-18 21:10:32 root:WARNING:This is a warning message
13-10-18 21:10:32 root:ERROR:This is an error message
13-10-18 21:10:32 root:CRITICAL:This is a critical message
但是當(dāng)發(fā)生異常時百揭,直接使用無參數(shù)的 debug()、info()奕剃、warning()衷旅、error()、critical() 方法并不能記錄異常信息纵朋,需要設(shè)置 exc_info 參數(shù)為 True 才可以柿顶,或者使用 exception() 方法,還可以使用 log() 方法操软,但還要設(shè)置日志級別和 exc_info 參數(shù)嘁锯。
import logging
logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
a = 5
b = 0
try:
c = a / b
except Exception as e:
# 下面三種方式三選一,推薦使用第一種
logging.exception("Exception occurred")
logging.error("Exception occurred", exc_info=True)
logging.log(level=logging.DEBUG, msg="Exception occurred", exc_info=True)
logging等級使用
級別 | 何時使用 |
---|---|
DEBUG |
詳細(xì)信息聂薪,一般只在調(diào)試問題時使用家乘。 |
INFO |
證明事情按預(yù)期工作。 |
WARNING |
某些沒有預(yù)料到的事件的提示藏澳,或者在將來可能會出現(xiàn)的問題提示仁锯。例如:磁盤空間不足。但是軟件還是會照常運行翔悠。 |
ERROR |
由于更嚴(yán)重的問題业崖,軟件已不能執(zhí)行一些功能了。 |
CRITICAL |
嚴(yán)重錯誤蓄愁,表明軟件已不能繼續(xù)運行了腻要。 |
級別 | 數(shù)字值 |
---|---|
CRITICAL |
50 |
ERROR |
40 |
WARNING |
30 |
INFO |
20 |
DEBUG |
10 |
NOTSET |
0 |
打印到控制臺
import logging
logging.debug('debug 信息')
logging.warning('只有這個會輸出。涝登。雄家。')
logging.info('info 信息')
由于默認(rèn)設(shè)置的等級是warning,所有只有warning的信息會輸出到控制臺。
WARNING:root:只有這個會輸出趟济。乱投。。
需求:
輸出log到控制臺以及將日志寫入log文件顷编。
保存2種類型的log戚炫, all.log 保存debug, info, warning, critical 信息, error.log則只保存error信息媳纬,同時按照時間自動分割日志文件双肤。
import logging
from logging import handlers
class Logger(object):
level_relations = {
'debug':logging.DEBUG,
'info':logging.INFO,
'warning':logging.WARNING,
'error':logging.ERROR,
'crit':logging.CRITICAL
}#日志級別關(guān)系映射
def __init__(self,filename,level='info',when='D',backCount=3,fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
self.logger = logging.getLogger(filename)
format_str = logging.Formatter(fmt)#設(shè)置日志格式
self.logger.setLevel(self.level_relations.get(level))#設(shè)置日志級別
sh = logging.StreamHandler()#往屏幕上輸出
sh.setFormatter(format_str) #設(shè)置屏幕上顯示的格式
th = handlers.TimedRotatingFileHandler(filename=filename,when=when,backupCount=backCount,encoding='utf-8')#往文件里寫入#指定間隔時間自動生成文件的處理器
#實例化TimedRotatingFileHandler
#interval是時間間隔,backupCount是備份文件的個數(shù)钮惠,如果超過這個個數(shù)茅糜,就會自動刪除,when是間隔的時間單位素挽,單位有以下幾種:
# S 秒
# M 分
# H 小時蔑赘、
# D 天、
# W 每星期(interval==0時代表星期一)
# midnight 每天凌晨
th.setFormatter(format_str)#設(shè)置文件里寫入的格式
self.logger.addHandler(sh) #把對象加到logger里
self.logger.addHandler(th)
if __name__ == '__main__':
log = Logger('all.log',level='debug')
log.logger.debug('debug')
log.logger.info('info')
log.logger.warning('警告')
log.logger.error('報錯')
log.logger.critical('嚴(yán)重')
Logger('error.log', level='error').logger.error('error')