標籤:first etl python ica file 建立 沒有 main 常見
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是常用的Tlog方式,所以直接包含在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中定義不繼承
python 自動化之路 logging日誌模組