python3_Logging模塊詳解

python的logging模塊提供了通用的日誌系統,能夠方便第三方模塊或應用使用。html

簡單使用

import logging

# logging.config.fileConfig("./logging.conf")
logging.basicConfig(filename='logger.log', level=logging.INFO)

# create logger
logger_name1 = "example01"
logger = logging.getLogger(logger_name1)

logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

控制檯無打印信息,發現當前工做目錄下生成了logger.log,內容爲:python

INFO:example01:info message
WARNING:example01:warn message
ERROR:example01:error message
CRITICAL:example01:critical message

該模塊能夠配置哪些信息:網絡

詳解:

Logging模塊提供Logger,handler,filter,formatter。less

  1. Logger:記錄器
    • 應用程序代碼能直接調用日誌接口。
    • Logger最經常使用的操做有兩類:配置和發送日誌消息。
    • 初始化 logger = logging.getLogger("endlesscode"),獲取logger對象,getLogger()方法後面最好加上所要日誌記錄的模塊名字,配置文件和打印日誌格式中的%(name)s 對應的是這裏的模塊名字,若是不指定name則返回root對象。
    • logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL這幾種級別,日誌會記錄設置級別以上的日誌
    • 級別 什麼時候使用
      DEBUG 詳細信息,典型地調試問題時會感興趣。
      INFO 證實事情按預期工做。
      WARNING 代表發生了一些意外,或者不久的未來會發生問題(如‘磁盤滿了’)。軟件仍是在正常工做。
      ERROR 因爲更嚴重的問題,軟件已不能執行一些功能了。
      CRITICAL 嚴重錯誤,代表軟件已不能繼續運行了。
    • 屢次使用相同的name調用getLogger方法返回同一個looger對象;
  2. handler:處理器
    • 將(記錄器產生的)日誌記錄(log record)發送至合適的目的地(destination),好比文件,socket等。
    • Handler經常使用的是StreamHandler和FileHandler,能夠簡單理解爲一個是console和文件日誌,一個打印在調試窗口上,一個記錄在一個文件上
    • 一個logger對象能夠經過addHandler方法添加0到多個handler,每一個handler又能夠定義不一樣日誌級別,以實現日誌分級過濾顯示。
  3. filter:
    • 過濾器,提供了更好的粒度控制,它能夠決定輸出哪些日誌記錄。。
  4. formatter:
    • 格式化器,指明瞭最終輸出中日誌記錄的佈局。
    • 指定日誌記錄輸出的具體格式。
    • formatter的構造方法須要兩個參數:消息的格式字符串和日期字符串,這兩個參數都是可選的。
    • 喜歡用這樣的格式 '[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S'

Logger 記錄器

Logger是一個樹形層級結構,在使用接口debug,info,warn,error,critical以前必須建立Logger實例socket

建立方法: logger = logging.getLogger(logger_name)函數

Handler 處理器

Handler處理器類型有不少種,比較經常使用的有三個,StreamHandlerFileHandlerNullHandler,詳情能夠訪問Python logging.handlers佈局

建立StreamHandler以後,能夠經過使用如下方法設置日誌級別,設置格式化器Formatter,增長或刪除過濾器Filter。學習

建立方法: sh = logging.StreamHandler(stream=None)

FileHandler

建立方法: fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)this

Formatter 格式化器

使用Formatter對象設置日誌信息最後的規則、結構和內容,默認的時間格式爲%Y-%m-%d %H:%M:%S。spa

建立方法: formatter = logging.Formatter(fmt=None, datefmt=None)

其中,fmt是消息的格式化字符串,datefmt是日期字符串。若是不指明fmt,將使用'%(message)s'。若是不指明datefmt,將使用ISO8601日期格式。

有用的format格式

格式 描述
%(levelno)s 打印日誌級別的數值
%(levelname)s 打印日誌級別名稱
%(pathname)s 打印當前執行程序的路徑
%(filename)s 打印當前執行程序名稱
%(funcName)s 打印日誌的當前函數
%(lineno)d 打印日誌的當前行號
%(asctime)s 打印日誌的時間
%(thread)d 打印線程id
%(threadName)s 打印線程名稱
%(process)d 打印進程ID
%(message)s 打印日誌信息

Filter 過濾器

Handlers和Loggers可使用Filters來完成比級別更復雜的過濾。Filter基類只容許特定Logger層次如下的事件。例如用‘A.B’初始化的Filter容許Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等記錄的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 若是用空字符串來初始化,全部的事件都接受。

建立方法: filter = logging.Filter(name='')

配置文件格式說明

%(name)s            Logger的名字
%(levelname)s       文本形式的日誌級別
%(message)s         用戶輸出的消息
%(asctime)s         字符串形式的當前時間。默認格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒
%(levelno)s         數字形式的日誌級別
%(pathname)s        調用日誌輸出函數的模塊的完整路徑名,可能沒有
%(filename)s        調用日誌輸出函數的模塊的文件名
%(module)s          調用日誌輸出函數的模塊名
%(funcName)s        調用日誌輸出函數的函數名
%(lineno)d          調用日誌輸出函數的語句所在的代碼行
%(created)f         當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌信息時來自Logger建立的毫秒數
%(thread)d          線程ID。可能沒有
%(threadName)s      線程名。可能沒有
%(process)d         進程ID。可能沒有

實例代碼:

一、在調試窗口上只打出error以上級別的日誌,可是在日誌中打出debug以上的信息
 1 import logging
 2 
 3 logger = logging.getLogger("simple_example")
 4 logger.setLevel(logging.DEBUG)
 5 
 6 # 創建一個filehandler來把日誌記錄在文件裏,級別爲debug以上
 7 fh = logging.FileHandler("spam.log")
 8 fh.setLevel(logging.DEBUG)
 9 
10 # 創建一個streamhandler來把日誌打在CMD窗口上,級別爲error以上
11 ch = logging.StreamHandler()
12 ch.setLevel(logging.ERROR)
13 
14 # 設置日誌格式
15 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
16 ch.setFormatter(formatter)
17 fh.setFormatter(formatter)
18 
19 # 將相應的handler添加在logger對象中
20 logger.addHandler(ch)
21 logger.addHandler(fh)
22 
23 # 開始打日誌
24 logger.debug("debug message")
25 logger.info("info message")
26 logger.warn("warn message")
27 logger.error("error message")
28 logger.critical("critical message")

將Logging封裝成一個類

這樣每次使用的時候,只要實例化一個對象就能夠了

 1 #! /usr/bin/python3
 2 
 3 import logging
 4 
 5 
 6 class Logger:
 7     def __init__(self, path, clevel=logging.DEBUG, Flevel=logging.DEBUG):
 8         self.logger = logging.getLogger(path)
 9         self.logger.setLevel(logging.DEBUG)
10         fmt = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
11 
12         # 設置CMD日誌
13         sh = logging.StreamHandler()
14         sh.setFormatter(fmt)
15         sh.setLevel(clevel)
16 
17         # 設置文件日誌
18         fh = logging.FileHandler(path)
19         fh.setFormatter(fmt)
20         fh.setLevel(Flevel)
21         self.logger.addHandler(sh)
22         self.logger.addHandler(fh)
23 
24     def debug(self, message):
25         self.logger.debug(message)
26 
27     def info(self, message):
28         self.logger.info(message)
29 
30     def war(self, message):
31         self.logger.warn(message)
32 
33     def error(self, message):
34         self.logger.error(message)
35 
36     def cri(self, message):
37         self.logger.critical(message)
38 
39 
40 if __name__ == '__main__':
41     logyyx = Logger('yyx.log', logging.ERROR, logging.DEBUG)
42     logyyx.debug('一個debug信息')
43     logyyx.info('一個info信息')
44     logyyx.war('一個warning信息')
45     logyyx.error('一個error信息')
46     logyyx.cri('一個致命critical信息')

調試運行結果:

/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/logging封裝/封裝爲一個類.py
[2017-11-07 10:25:50] [ERROR] 一個error信息
[2017-11-07 10:25:50] [CRITICAL] 一個致命critical信息

日誌文件內的內容:

[2017-11-07 10:25:50] [DEBUG] 一個debug信息
[2017-11-07 10:25:50] [INFO] 一個info信息
[2017-11-07 10:25:50] [WARNING] 一個warning信息
[2017-11-07 10:25:50] [ERROR] 一個error信息
[2017-11-07 10:25:50] [CRITICAL] 一個致命critical信息

 

多模塊使用logging

logging模塊保證在同一個python解釋器內,屢次調用logging.getLogger('log_name')都會返回同一個logger實例,即便是在多個模塊的狀況下。因此典型的多模塊場景下使用logging的方式是在main模塊中配置logging,這個配置會做用於多個的子模塊,而後在其餘模塊中直接經過getLogger獲取Logger對象便可。

 

logging.conf:

[loggers] 
keys=root,main 
  
[handlers] 
keys=consoleHandler,fileHandler 
  
[formatters] 
keys=fmt 
  
[logger_root] 
level=DEBUG 
handlers=consoleHandler 
  
[logger_main] 
level=DEBUG 
qualname=main 
handlers=fileHandler 
  
[handler_consoleHandler] 
class=StreamHandler 
level=DEBUG 
formatter=fmt 
args=(sys.stdout,) 
  
[handler_fileHandler] 
class=logging.handlers.RotatingFileHandler 
level=DEBUG 
formatter=fmt 
args=('tst.log','a',20000,5,) 
  
[formatter_fmt] 
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 
datefmt= 

主模塊main.py

 1 import logging
 2 import logging.config
 3 import mod
 4 
 5 logging.config.fileConfig('logging.conf')
 6 root_logger = logging.getLogger('root')
 7 root_logger.debug('test root logger...')
 8 
 9 logger = logging.getLogger('main')
10 logger.info('test main logger')
11 logger.info('start import module \'mod\'...')
12 
13 
14 logger.debug('let\'s test mod.testLogger()')
15 mod.testLogger()
16 
17 root_logger.info('finish test...')

子模塊mod.py

import logging
import submod

logger = logging.getLogger('main.mod')
logger.info('logger of mod say something...')


def testLogger():
    logger.debug('this is mod.testLogger...')
    submod.tst() 

子子模塊submod.py:

import logging

logger = logging.getLogger('main.mod.submod')
logger.info('logger of submod say something...')


def tst():
    logger.info('this is submod.tst()...')

而後運行python main.py,控制檯輸出:

/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/logging封裝/main.py
2017-11-07 10:53:49,228 - root - DEBUG - test root logger...
2017-11-07 10:53:49,228 - main - INFO - test main logger
2017-11-07 10:53:49,228 - main - INFO - start import module 'mod'...
2017-11-07 10:53:49,229 - main - DEBUG - let's test mod.testLogger()
2017-11-07 10:53:49,229 - main.mod - DEBUG - this is mod.testLogger...
2017-11-07 10:53:49,229 - main.mod.submod - INFO - this is submod.tst()...
2017-11-07 10:53:49,229 - root - INFO - finish test...

再看一下tst.log,logger配置中的輸出的目的地:

2017-11-07 10:56:54,189 - main - INFO - test main logger
2017-11-07 10:56:54,189 - main - INFO - start import module 'mod'...
2017-11-07 10:56:54,189 - main - DEBUG - let's test mod.testLogger()
2017-11-07 10:56:54,189 - main.mod - DEBUG - this is mod.testLogger...
2017-11-07 10:56:54,190 - main.mod.submod - INFO - this is submod.tst()...

tst.log中沒有root logger輸出的信息,由於logging.conf中配置了只有main logger及其子logger使用RotatingFileHandler,而root logger是輸出到標準輸出。

 

 

學習思考思路參考:

Logging工做流程

logging模塊使用過程

  1. 第一次導入logging模塊或使用reload函數從新導入logging模塊,logging模塊中的代碼將被執行,這個過程當中將產生logging日誌系統的默認配置。
  2. 自定義配置(可選)。logging標準模塊支持三種配置方式: dictConfig,fileConfig,listen。其中,dictConfig是經過一個字典進行配置Logger,Handler,Filter,Formatter;fileConfig則是經過一個文件進行配置;而listen則監聽一個網絡端口,經過接收網絡數據來進行配置。固然,除了以上集體化配置外,也能夠直接調用Logger,Handler等對象中的方法在代碼中來顯式配置。
  3. 使用logging模塊的全局做用域中的getLogger函數來獲得一個Logger對象實例(其參數便是一個字符串,表示Logger對象實例的名字,即經過該名字來獲得相應的Logger對象實例)。
  4. 使用Logger對象中的debug,info,error,warn,critical等方法記錄日誌信息。

logging模塊處理流程


logging_flow.png
  1. 判斷日誌的等級是否大於Logger對象的等級,若是大於,則往下執行,不然,流程結束。
  2. 產生日誌。第一步,判斷是否有異常,若是有,則添加異常信息。第二步,處理日誌記錄方法(如debug,info等)中的佔位符,即通常的字符串格式化處理。
  3. 使用註冊到Logger對象中的Filters進行過濾。若是有多個過濾器,則依次過濾;只要有一個過濾器返回假,則過濾結束,且該日誌信息將丟棄,再也不處理,而處理流程也至此結束。不然,處理流程往下執行。
  4. 在當前Logger對象中查找Handlers,若是找不到任何Handler,則往上到該Logger對象的父Logger中查找;若是找到一個或多個Handler,則依次用Handler來處理日誌信息。但在每一個Handler處理日誌信息過程當中,會首先判斷日誌信息的等級是否大於該Handler的等級,若是大於,則往下執行(由Logger對象進入Handler對象中),不然,處理流程結束。
  5. 執行Handler對象中的filter方法,該方法會依次執行註冊到該Handler對象中的Filter。若是有一個Filter判斷該日誌信息爲假,則此後的全部Filter都再也不執行,而直接將該日誌信息丟棄,處理流程結束。
  6. 使用Formatter類格式化最終的輸出結果。 注:Formatter同上述第2步的字符串格式化不一樣,它會添加額外的信息,好比日誌產生的時間,產生日誌的源代碼所在的源文件的路徑等等。
  7. 真正地輸出日誌信息(到網絡,文件,終端,郵件等)。至於輸出到哪一個目的地,由Handler的種類來決定。
 
參考文章:
http://www.jb51.net/article/88449.htm
http://www.jianshu.com/p/feb86c06c4f4
相關文章
相關標籤/搜索