logging 日志模塊
http://python.usyiyi.cn/python_278/library/logging.html 中文官方
http://blog.csdn.net/zyz511919766/article/details/25136485 清晰明了,入門必備
http://my.oschina.net/leejun2005/blog/126713 繼承講的很棒
http://my.oschina.net/u/126495/blog/464892 實例分析
一:概述
在實際項目中,需要對一些數據進行日志記錄,並將日志記錄到不同的存儲單元中,例如數據庫,文本,或者推送到圖形化界面中,當需要時發現自己實現一個日
志庫其實是要很大的代價,因此,第三方的日志庫上進行定制化處理 正文內容是對logging的理解和使用方式,非常方便
1:四個主要類,使用官方文檔中的概括:
logger 提供了應用程序可以直接使用的接口;
handler 將(logger創建的)日志記錄發送到合適的目的輸出;
filter 提供了細度設備來決定輸出哪條日志記錄;用處不太大
formatter 決定日志記錄的最終輸出格式
2:模塊級函數
logging.getLogger([name]): #返回一個logger對象,如果沒有指定名字將返回root logger,最常用
logging.basicConfig(): #給logger對象的配置管理函數 ,不常用
logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical(): #logger的日志級別
二: logging工作流演示
cat demo.py
#coding:utf-8
import logging
# 創建一個logger命名為mylogger, %(name)s可調用這個名字
mylogger = logging.getLogger('mylogger')
mylogger.setLevel(logging.DEBUG)
# 定義日志輸出格式formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(filename)s- %(levelname)s - %(message)s')
# 創建一個handler,用於寫入日志文件,只輸出debug級別以上的日志,並調用定義的輸出格式
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
# 再創建一個handler,用於輸出到控制台, 一般不用
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
# 給我們開始實例化的logger對象添加handler
mylogger.addHandler(fh)
mylogger.addHandler(ch)
# 直接在本模塊中調用記錄兩條日志——生產環境會封裝成函數調用
mylogger.info('foorbar')
mylogger.debug('just a test ')
$ python demo.py
2015-10-30 15:44:01,722 - mylogger - test1.py- INFO - foorbar
2015-10-30 15:44:01,723 - mylogger - test1.py- DEBUG - just a test
http://www.php101.cn/2015/03/05/Python%E4%B8%AD%E7%9A%84Logging%E7%AE%80%E4%BB%8B/ 精彩實例
三:logging模塊的API
1:logging.getLogger([name])
返回一個logger實例,如果沒有指定name,返回root logger。只要name相同,返回的logger實例都是同一個而且只有一個,即name和logger實例是一一對
應的。這意味着,無需把logger實例在各個模塊中傳遞。只要知道name,就能得到同一個logger實例
2:logger.setLevel(lvl)——設置logger的level,
level有以下幾個級別:
NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
不同日志級別對應的數字對照
級別 數值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0
如果把logger的級別設置為INFO, 那么小於INFO級別的日志都不輸出, 大於等於INFO級別的日志都輸出。也就意味着同一個logger實例,如果多個地方調用,會
出現很多重復的日志
logger.debug("debug") # 不輸出
logger.info("info") # 輸出
logger.warning("warning") # 輸出
logger.error("error") # 輸出
logger.critical("critical") # 輸出
3:logger.addHandler(hd)—— logger雇佣handler來幫它處理日志
handler對象負責發送相關的信息到指定目的地。Python的日志系統有多種Handler可以使用。有些Handler可以把信息輸出到控制台,有些Logger可以把信息輸
出到文件,還有些 Handler可以把信息發送到網絡上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler()方法添加多個多handler
handler主要有以下幾種:
logging.StreamHandler: #日志輸出到流即控制台,可以是sys.stderr、sys.stdout
logging.FileHandler: #日志輸出到文件
logging.handlers.RotatingFileHandler #日志輸出到文件,並按照設定的日志文件大小切割
logging.handlers.TimedRotatingFileHandler #日志輸出到文件,並按設定的時間切割日志文件
logging.handlers.SocketHandler: #遠程輸出日志到TCP/IP sockets
logging.handlers.DatagramHandler: #遠程輸出日志到UDP sockets
logging.handlers.SMTPHandler: #遠程輸出日志到郵件地址
logging.handlers.SysLogHandler: #日志輸出到syslog
logging.handlers.NTEventLogHandler: #遠程輸出日志到Windows NT/2000/XP的事件日志
logging.handlers.MemoryHandler: #日志輸出到內存中的制定buffer
由於StreamHandler和FileHandler是常用的日志處理方式,所以直接包含在logging模塊中,而其他方式則包含在logging.handlers模塊中,
handle常見調用
Handler.setLevel(lel) #指定被處理的信息級別,低於lel級別的信息將被忽略
Handler.setFormatter() #給這個handler選擇一個格式
Handler.addFilter(filt)、Handler.removeFilter(filt):#新增或刪除一個filter對象
舉兩個栗子
1: logging內部調用——測試學習即可
cat demo.py
import logging
import logging.handlers
LOG_FILE = 'api.log'
#定義日志文件切割規則最多備份5個日志文件,每個日志文件最大10M
handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes = 10*1024*1024, backupCount = 5)
#handler=logging.handlers.TimedRotatingFileHandler(LOG_FILE, when='midnight') #每天零點切換
#定義日志輸出格式
fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)s'
formatter = logging.Formatter(fmt) # 實例化formatter
handler.setFormatter(formatter) # 為handler添加formatter
#實例化一個logger對象,並為其綁定handle
mylogger = logging.getLogger('test')
mylogger.addHandler(handler) # 為mylogger添加handler
mylogger.setLevel(logging.DEBUG) # 為mylogger設置輸出級別
#調用mylogger
mylogger.info('first info message')
mylogger.debug('first debug message')
2: logging封裝為函數——適用生產環境中
cat util.py
#/usr/bin/env python
#coding:utf-8
import logging,logging.handlers
def WriteLog(log_name):
log_filename = "/tmp/test.log"
log_level = logging.DEBUG
format = logging.Formatter('%(asctime)s %(filename)s [line:%(lineno)2d]-%(funcName)s %(levelname)s %(message)s')
handler = logging.handlers.RotatingFileHandler(log_filename, mode='a', maxBytes=10*1024*1024, backupCount=5)
handler.setFormatter(format)
logger = logging.getLogger(log_name)
logger.addHandler(handler)
logger.setLevel(log_level)
return logger #函數最終將實例化的logger對象返回,后面直接調用即可
if __name__ == "__main__":
WriteLog('api').info('123') #模塊內部直接調用函數。等價下面兩行,下面的方法不推薦
# writelog = WriteLog('api')
# writelog.info('123')
'''
外面程序調用函數
cat test.py
import util
def index()
util.WriteLog('api').info('123')
index()
'''
4:logging.basicConfig([**kwargs])——加載logger的各項配置參數,不好用
cat test.py
import logging
logging.basicConfig(level=logging.DEBUG, #輸出debug及其級別更高級別的日志
format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%d %b %Y %H:%M:%S',
filename='myapp.log', #日志文件輸出的文件地址,不寫默認打印到桌面
filemde='w')
logging.debug("this is debug message")
logging.info("this is info message")
logging.warning("this is warning message")
tail -f myapp.log
30 Oct 2015 14:18:51 test.py [line:8] DEBUG this is debug message
30 Oct 2015 14:18:51 test.py [line:9] INFO this is info message
30 Oct 2015 14:18:51 test.py [line:10] WARNING this is warning message
關於logging.basicConfig函數的常用配置:
filename: 指定日志文件名
filemode: 和file函數意義相同,指定日志文件的打開模式,'w'或'a'
datefmt: 指定時間格式,同time.strftime()
level: 設置日志級別,默認為logging.WARNING,即warning及級別更高的日志才輸出
stream: 指定將日志的輸出流,可以指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,當stream和filename同時指定時,stream被
忽略
format: 指定輸出的格式和內容,format可以輸出很多有用信息,如上例所示:
%(name)s 打印logger名,默認為root
%(levelno)s: 打印日志級別的數值
%(levelname)s: 打印日志級別名稱
%(pathname)s: 打印當前執行程序的路徑,其實就是sys.argv[0]
%(filename)s: 打印當前執行程序名
%(funcName)s: 打印日志的當前函數
%(lineno)d: 打印日志的當前行號
%(asctime)s: 打印日志的時間
%(message)s: 打印日志信息
%(thread)d: 打印線程ID
%(threadName)s: 打印線程名稱
%(process)d: 打印進程ID
5: logging.config模塊通過配置文件的方式,加載logger的參數——最好用的方式
cat logger.conf
# 定義logger模塊,root是父類,必需存在的,其它的是自定義。
# logging.getLogger(NAME)就相當於向logging模塊注冊了實例化了
# name 中用 . 表示 log 的繼承關系
[loggers]
keys=root,example01,example02
# [logger_xxxx] logger_模塊名稱
# level 級別,級別有DEBUG、INFO、WARNING、ERROR、CRITICAL
# handlers 處理類,可以有多個,用逗號分開
# qualname logger名稱,應用程序通過 logging.getLogger獲取。對於不能獲取的名稱,則記錄到root模塊。
# propagate 是否繼承父類的log信息,0:否 1:是
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
# [handler_xxxx]
# class handler類名
# level 日志級別
# formatter,上面定義的formatter
# args handler初始化函數參數
[handlers]
keys=hand01,hand02,hand03
[handler_hand01]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('myapp.log', 'a')
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form02
args=('myapp.log', 'a', 10*1024*1024, 5)
# 日志格式
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(asctime)s%(name)-12s: %(levelname)-8s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
調用
import logging
import logging.config
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example01")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')
生產環境中使用案例——通過函數定義調用
A:定義到工具模塊中
cat util.py
import logging,
import logging.config
def write_log(loggername):
work_dir = os.path.dirname(os.path.realpath(__file__))
log_conf= os.path.join(work_dir, 'conf/logger.conf')
logging.config.fileConfig(log_conf)
logger = logging.getLogger(loggername)
return logger
B: 外部模塊調用
cat test.py
import util
util.write_log('api').info('just a test')
四: 關於root logger以及logger的父子關系
logger實例之間還有父子關系, root logger就是處於最頂層的logger, 它是所有logger的祖先。如下圖:
如何得到root logger
root logger是默認的logger如果不創建logger實例, 直接調用logging.debug()、logging.info()logging.warning(),logging.error()、logging.critical()這
些函數,
那么使用的logger就是 root logger, 它可以自動創建,也是單實例的。
root logger的日志級別
root logger默認的level是logging.WARNING
如何表示父子關系
logger的name的命名方式可以表示logger之間的父子關系. 比如:
parent_logger = logging.getLogger('foo')
child_logger = logging.getLogger('foo.bar')
什么是effective level
logger有一個概念,叫effective level。 如果一個logger沒有顯示地設置level,那么它就
用父親的level。如果父親也沒有顯示地設置level, 就用父親的父親的level,以此推....
最后到達root logger,一定設置過level。默認為logging.WARNING
child loggers得到消息后,既把消息分發給它的handler處理,也會傳遞給所有祖先logger處理,
eg:
import logging
# 設置root logger,祖先
r = logging.getLogger()
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
r.addHandler(ch)
# 創建一個logger作為父親
p = logging.getLogger('foo')
p.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(message)s')
ch.setFormatter(formatter)
p.addHandler(ch)
# 創建一個孩子logger
c = logging.getLogger('foo.bar')
c.debug('foo')
輸出:
2011-08-31 21:04:29,893 - foo #父親處理
2011-08-31 21:04:29,893 - DEBUG - foo #祖先處理
可見, 孩子logger沒有任何handler,所以對消息不做處理。但是它把消息轉發給了它的父親以及root logger。最后輸出兩條日志。
這也就出現了一個問題,同一條日志會重復輸出
解決方案
1:每個logger實例都給一個獨立的名字,輸出之間互不影響,
2: logging.conf中定義不繼承