python 自動化之路 logging日誌模塊

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 實例分析php

一:概述


在實際項目中,須要對一些數據進行日誌記錄,並將日誌記錄到不一樣的存儲單元中,例如數據庫,文本,或者推送到圖形化界面中,當須要時發現本身實現一個日
志庫實際上是要很大的代價,所以,第三方的日誌庫上進行定製化處理 正文內容是對logging的理解和使用方式,很是方便
1:四個主要類,使用官方文檔中的歸納:html


  logger 提供了應用程序能夠直接使用的接口;
  handler 將(logger建立的)日誌記錄發送到合適的目的輸出;
  filter 提供了細度設備來決定輸出哪條日誌記錄;用處不太大
  formatter 決定日誌記錄的最終輸出格式python


2:模塊級函數數據庫


  logging.getLogger([name]): #返回一個logger對象,若是沒有指定名字將返回root logger,最經常使用
  logging.basicConfig(): #給logger對象的配置管理函數 ,不經常使用
  logging.debug()logging.info()logging.warning()logging.error()logging.critical(): #logger的日誌級別api

 

二: 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
因爲StreamHandlerFileHandler是經常使用的日誌處理方式,因此直接包含在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')app

四: 關於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中定義不繼承socket

相關文章
相關標籤/搜索