python使用logging進行日誌配置

1. logging介紹

 

Python的logging模塊提供了通用的日誌系統,能夠方便第三方模塊或者是應用使用。這個模塊提供不一樣的日誌級別,並能夠採用不一樣的方式記錄日誌,好比文件,HTTP GET/POST,SMTP,Socket等,甚至能夠本身實現具體的日誌記錄方式。python

logging模塊與log4j的機制是同樣的,只是具體的實現細節不一樣。正則表達式

模塊提供logger,handler,filter,formatter:app

1)logger:提供日誌接口,供應用代碼使用。logger最長用的操做有兩類:配置和發送日誌消息。能夠經過logging.getLogger(name)獲取logger對象,若是不指定name則返回root對象,屢次使用相同的name調用getLogger方法返回同一個logger對象。socket

2)handler:將日誌記錄(log record)發送到合適的目的地(destination),好比文件,socket等。一個logger對象能夠經過addHandler方法添加0到多個handler,每一個handler又能夠定義不一樣日誌級別,以實現日誌分級過濾顯示。函數

3)filter:提供一種優雅的方式決定一個日誌記錄是否發送到handler。this

4)formatter:指定日誌記錄輸出的具體格式。formatter的構造方法須要兩個參數:消息的格式字符串和日期字符串,這兩個參數都是可選的。spa

與log4j相似,logger,handler和日誌消息的調用能夠有具體的日誌級別(Level),只有在日誌消息的級別大於logger和handler的級別。.net

 

RotatingFileHandler:debug

 

#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# ithomer.net

import logging
import logging.handlers

LOG_FILE = 'tst.log'

handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes = 1024*1024, backupCount=5)

fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
handler.setFormatter(formatter)

logger = logging.getLogger('tst')
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

logger.info('info msg')
logger.debug('debug msg')

 

運行結果(tst.log日誌文件內容):
2013-11-28 21:19:25,442 - logtest3.py:21 - tst - INFO - info msg
2013-11-28 21:19:25,442 - logtest3.py:22 - tst - DEBUG - debug msg日誌

 

TimedRotatingFileHandler:

 

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
import logging.handlers

def main():
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    
    handler = logging.handlers.TimedRotatingFileHandler("test.log", 'D')
    # fmt = logging.Formatter("%(asctime)s - %(pathname)s - %(filename)s - %(funcName)s - %(lineno)s - %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S")
    fmt = logging.Formatter("%(asctime)s - %(pathname)s - %(filename)s - %(funcName)s - %(lineno)s - %(levelname)s - %(message)s")
    handler.setFormatter(fmt)
    logger.addHandler(handler)
    
    logger.debug("debug msg")
    logger.info("info msg")
    logger.warn("warn msg")
    logger.warning("warning msg")
    logger.error("error msg")
    logger.fatal("fatal msg")
    logger.critical("critical msg")

if __name__ == "__main__":
    main()

運行結果:

 

 

2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 17 - DEBUG - debug msg
2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 18 - INFO - info msg
2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 19 - WARNING - warn msg
2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 20 - WARNING - warning msg
2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 21 - ERROR - error msg
2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 22 - CRITICAL - fatal msg
2013-12-31 17:39:07,093 - /home/homer/workspace/myPython/com/mylogging.py - mylogging.py - main - 23 - CRITICAL - critical msg

 

 

關於formatter的配置,採用的是%(<dict key>)s的形式,就是字典的關鍵字替換。提供的關鍵字包括:

 

 

Format Description
%(name)s Name of the logger (logging channel).
%(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).
%(levelname)s Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
%(pathname)s Full pathname of the source file where the logging call was issued (if available).
%(filename)s Filename portion of pathname.
%(module)s Module (name portion of filename).
%(funcName)s Name of function containing the logging call.
%(lineno)d Source line number where the logging call was issued (if available).
%(created)f Time when the LogRecord was created (as returned by time.time()).
%(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
%(asctime)s Human-readable time when the LogRecord was created. By default this is of the form 「2003-07-08 16:49:45,896」 (the numbers after the comma are millisecond portion of the time).
%(msecs)d Millisecond portion of the time when the LogRecord was created.
%(thread)d Thread ID (if available).
%(threadName)s Thread name (if available).
%(process)d Process ID (if available).
%(message)s The logged message, computed as msg % args.

 

 

 

2. logging的配置

logging的配置能夠採用python代碼或是配置文件。python代碼的方式就是在應用的主模塊中,構建handler,handler,formatter等對象。而配置文件的方式是將這些對象的依賴關係分離出來放在文件中。好比前面的例子就相似於python代碼的配置方式。這裏看一下采用配置文件的方式。

 

#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# ithomer.net

import logging
import logging.config

LOG_FILE = 'tst.log'

logging.config.fileConfig('logging.conf')

# instance of logging
logger = logging.getLogger('tst')

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

 

loggin.conf採用了模式匹配的方式進行配置,正則表達式是r'^[(.*)]$',從而匹配出全部的組件。對於同一個組件具備多個實例的狀況使用逗號‘,’進行分隔。對於一個實例的配置採用componentName_instanceName配置塊。使用這種方式仍是蠻簡單的。

 

[loggers]
keys=root,tst

[handlers]
keys=consoleHandler

[formatters]
keys=tstFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_tst]
level=DEBUG
handlers=consoleHandler
qualname=tst
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=tstFormatter
args=(sys.stdout,)

[formatter_tstFormatter]
format=%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(levelname)s - %(message)s
datefmt=

在指定handler的配置時,class是具體的handler類的類名,能夠是相對logging模塊或是全路徑類名,好比須要RotatingFileHandler,則class的值能夠爲:RotatingFileHandler或者logging.handlers.RotatingFileHandler。args就是要傳給這個類的構造方法的參數,就是一個元組,按照構造方法聲明的參數的順序。

 

運行結果,控制檯輸出:

2013-11-28 23:02:32,574 - logtest4.py:16 - tst - DEBUG - debug msg
2013-11-28 23:02:32,574 - logtest4.py:17 - tst - INFO - info msg
2013-11-28 23:02:32,574 - logtest4.py:18 - tst - WARNING - warn msg
2013-11-28 23:02:32,574 - logtest4.py:19 - tst - ERROR - error msg
2013-11-28 23:02:32,574 - logtest4.py:20 - tst - CRITICAL - critical msg

這裏還要明確一點,logger對象是有繼承關係的,好比名爲a.b和a.c的logger都是名爲a的子logger,而且全部的logger對象都繼承於root。若是子對象沒有添加handler等一些配置,會從父對象那繼承。這樣就能夠經過這種繼承關係來複用配置。

 

 

3. 多模塊使用logging

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

這裏使用上面配置文件:

 

[loggers]
keys=root,main

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=fmt

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_main]
level=DEBUG
handlers=fileHandler
qualname=main
#propagate=0

[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 - %(filename)s:%(lineno)s - %(name)s - %(levelname)s - %(message)s
datefmt=

主模塊main.py:

 

 

#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# main.py
#
# ithomer.net

import logging
import logging.config


logging.config.fileConfig('logging.conf')

# root
logger_root = logging.getLogger('root')
logger_root.debug('debug root')

# main
logger_main = logging.getLogger('main')
logger_main.info('info main')

logger_main.info('start import module \'mod\'...')
import mod
logger_main.debug('test mod.testLogger()')
mod.testLogger()

logger_root.info('the end!')

 

子模塊mod.py:

 

#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# mod.py
#
# ithomer.net

import logging

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

def testLogger():
    logger.debug('this is main.mod testLogging()')
    logger.info('start import module \'submod\'...')
    import submod
    submod.testLogger()

 

子子模塊submod.py:

 

#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# submod.py
#
# ithomer.net

import logging

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

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

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

 

 

2013-11-28 16:58:15,517 - main.py:16 - root - DEBUG - debug root
2013-11-28 16:58:15,517 - main.py:20 - main - INFO - info main
2013-11-28 16:58:15,517 - main.py:22 - main - INFO - start import module 'mod'...
2013-11-28 16:58:15,518 - mod.py:11 - main.mod - INFO - info of main.mod
2013-11-28 16:58:15,518 - main.py:24 - main - DEBUG - test mod.testLogger()
2013-11-28 16:58:15,518 - mod.py:14 - main.mod - DEBUG - this is main.mod testLogging()
2013-11-28 16:58:15,519 - mod.py:15 - main.mod - INFO - start import module 'submod'...
2013-11-28 16:58:15,519 - submod.py:11 - main.mod.submod - INFO - info of main.mod.submod
2013-11-28 16:58:15,519 - submod.py:14 - main.mod.submod - DEBUG - this is main.mod.submod testLogging()
2013-11-28 16:58:15,519 - main.py:27 - root - INFO - the end!

 

能夠看出,和預想的同樣,而後在看一下tst.log,logger配置中的輸出的目的地:

 

2013-11-28 16:58:15,517 - main.py:20 - main - INFO - info main
2013-11-28 16:58:15,517 - main.py:22 - main - INFO - start import module 'mod'...
2013-11-28 16:58:15,518 - mod.py:11 - main.mod - INFO - info of main.mod
2013-11-28 16:58:15,518 - main.py:24 - main - DEBUG - test mod.testLogger()
2013-11-28 16:58:15,518 - mod.py:14 - main.mod - DEBUG - this is main.mod testLogging()
2013-11-28 16:58:15,519 - mod.py:15 - main.mod - INFO - start import module 'submod'...
2013-11-28 16:58:15,519 - submod.py:11 - main.mod.submod - INFO - info of main.mod.submod
2013-11-28 16:58:15,519 - submod.py:14 - main.mod.submod - DEBUG - this is main.mod.submod testLogging()

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

 

 

 

一、從一個使用場景開始

開發一個日誌系統, 既要把日誌輸出到控制檯, 還要寫入日誌文件

 

#!/usr/bin/python
# ithomer in 2013

import logging

# create instance of logging
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)


# file handler
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)


# console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)


# formatter
fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(fmt)
ch.setFormatter(fmt)


# add formatter to handler
logger.addHandler(fh)
logger.addHandler(ch)


# print a log
logger.info('ithomer')

運行後, 在控制檯和日誌文件(test.log)都有一條日誌:

 

2013-11-27 23:45:20,329 - mylogger - INFO - ithomer

 

 

二、logging模塊的API

結合上面的例子,咱們說下幾個最常使用的API

logging.getLogger([name])

返回一個logger實例,若是沒有指定name,返回root logger。只要name相同,返回的logger實例都是同一個並且只有一個,即name和logger實例是一一對應的。這意味着,無需把logger實例在各個模塊中傳遞。只要知道name,就能獲得同一個logger實例

 

Logger.setLevel(lvl)

設置logger的level, level有如下幾個級別:

>>> import logging
>>> print logging.NOTSET
0
>>> print logging.DEBUG
10
>>> print logging.INFO
20
>>> print logging.WARNING
30
>>> print logging.ERROR
40
>>> print logging.CRITICAL
50

NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL

 

若是把looger的級別設置爲INFO, 那麼小於INFO級別的日誌都不輸出, 大於等於INFO級別的日誌都輸出

logger.debug("foobar")        # 不輸出   
logger.info("foobar")            # 輸出  
logger.warning("foobar")    # 輸出  
logger.error("foobar")          # 輸出  
logger.critical("foobar")       # 輸出  

 

Logger.addHandler(hdlr)

logger能夠僱傭handler來幫它處理日誌, handler主要有如下幾種:

StreamHandler         # 輸出到控制檯

FileHandler                # 輸出到文件

handler還能夠設置本身的level以及輸出格式。

 

logging.basicConfig([**kwargs])

這個函數用來配置root logger, 爲root logger建立一個StreamHandler,設置默認的格式。

這些函數: logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical() 若是調用的時候發現root logger沒有任何 handler, 會自動調 basicConfig添加一個handler, 若是root logger已有handler, 這個函數不作任何事情,使用basicConfig來配置root logger的輸出格式和level:

import logging  
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)  
logging.debug('This message should appear on the console')  

 

 

三、root logger與logger 父子關係

前面屢次提到root logger, 實際上logger實例之間還有父子關係, root logger就是處於最頂層的logger, 它是全部logger的祖先。以下圖:

 

root logger是默認的logger,若是不建立logger實例, 直接調用logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()這些函數,那麼使用的logger就是 root logger, 它能夠自動建立,也是單實例的。

 

如何獲得root logger

經過logging.getLogger()或者logging.getLogger("")獲得root logger實例。

 

默認的level

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處理,來看一個例子:

 

#/usr/bin/python
# ithomer in 2013

import logging

# root logger
root = logging.getLogger()      # root

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)

root.addHandler(ch)


# logging as parent
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)


# logging as child
c = logging.getLogger('foo.bar')
c.debug('foo')

 

運行結果:

2013-11-27 22:40:56,699 - foo
2013-11-27 22:40:56,699 - DEBUG - foo

 

可見, 孩子logger沒有任何handler,因此對消息不作處理,可是它把消息轉發給了它的父親以及root logger,最後輸出兩條日誌,分別是p和root 輸出的logging。

相關文章
相關標籤/搜索