2011-05-12 63 views
33

我有一個簡單的Python腳本,使用內置的loggingPython - 避免在函數之間傳遞記錄器引用?

我正在配置函數內的日誌記錄。基本結構是這樣的:

#!/usr/bin/env python 
import logging 
import ... 

def configure_logging(): 
    logger = logging.getLogger("my logger") 
    logger.setLevel(logging.DEBUG) 
    # Format for our loglines 
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 
    # Setup console logging 
    ch = logging.StreamHandler() 
    ch.setLevel(logging.DEBUG) 
    ch.setFormatter(formatter) 
    logger.addHandler(ch) 
    # Setup file logging as well 
    fh = logging.FileHandler(LOG_FILENAME) 
    fh.setLevel(logging.DEBUG) 
    fh.setFormatter(formatter) 
    logger.addHandler(fh) 
    return logger 

def count_parrots(): 
    ... 
    logger.debug?? 

if __name__ == '__main__': 
    logger = configure_logging() 
    logger.debug("I'm a log file") 
    parrots = count_parrots() 

我可以從裏面__main__調用記錄的罰款。但是,如何從count_parrots()函數內部調用記錄器?處理像這樣配置記錄器的最pythonic方式是什麼?

回答

32

您可以使用根(默認)記錄器,因此模塊級別功能logging.debug,...或使用它的函數中獲取您的記錄器。 事實上,getLogger函數是一個類似工廠的函數,它具有註冊表(單例),即它始終爲給定的記錄器名稱返回相同的實例。 您可以通過簡單地使用

logger = logging.getLogger("my logger") 

之初從而獲得count_parrots你的記錄。但是,慣例是爲記錄器使用點分層名稱。見http://docs.python.org/library/logging.html#logging.getLogger

編輯:

您可以使用一個裝飾來記錄行爲添加到您的個性化功能,例如:

def debug(loggername): 
    logger = logging.getLogger(loggername) 
    def log_(enter_message, exit_message=None): 
     def wrapper(f): 
      def wrapped(*args, **kargs): 
       logger.debug(enter_message) 
       r = f(*args, **kargs) 
       if exit_message: 
        logger.debug(exit_message) 
       return r 
      return wrapped 
     return wrapper 
    return log_ 

my_debug = debug('my.logger') 

@my_debug('enter foo', 'exit foo') 
def foo(a, b): 
    return a+b 

可以「硬編碼」記錄器名稱,取下頂關閉級別和my_debug。

+5

好的,所以我可以在每個需要記錄的函數的開頭調用logging.getLogger。似乎有點浪費和重複肯定?很公平。或者,我會更好地面向對象,並試圖將整個過程整合到一個類中去? (我知道這是一個非常普遍的問題,我只是在尋找Python世界中的成就)。 – victorhooi 2011-05-17 02:30:54

+0

你可以把你的函數放在一個類中,使用logger作爲實例變量,或者(我更喜歡)創建一個裝飾器,將日誌函數添加到你的單個函數中 – 2011-05-19 12:17:16

+9

這個答案顯示了Python'logging'幾乎所有的錯誤。模塊... – rkrzr 2014-04-30 15:04:17

-3

您可以給logger作爲count_parrots()的參數或者,我會做什麼,創建類鸚鵡並使用記錄器作爲其方法之一。

6

處理日誌記錄的典型方法是將每個模塊的記錄器存儲在全局變量中。該模塊中的任何函數和方法都只引用同一個記錄器實例。

這是在介紹簡要討論到文檔中的提前採伐教程: http://docs.python.org/howto/logging.html#advanced-logging-tutorial

可以通記錄身邊的實例作爲參數,但這樣做通常是罕見的。

+0

我認爲標準做法是使用logger = logging.getLogger(「logger.name」) – 2011-05-12 07:33:49

+0

在模塊級別,當然。儘管使用單獨的記錄器來處理同一模塊中的不同功能和方法通常是矯枉過正的。一個例外是使用單獨的記錄器可以是一種記錄哪些線程記錄特定事件的非常簡單的方法。 – ncoghlan 2011-05-12 10:50:53

+0

啊。我以爲你的意思是使用'global'關鍵字。 – 2011-05-12 11:08:59

13

你可以這樣做:

logger = logging.getLogger("my logger") 

count_parrots()方法。當您傳遞之前使用的名稱(即「我的記錄器」)時,日誌記錄模塊將返回與該名稱相對應的相同實例。

更新:從logging tutorial (emphais礦)

getLogger()返回如果 不具有指定 名稱的引用到 記錄器實例,如果提供,或根。這些名稱是以期限分隔的 分層結構。多個 調用getLogger()與相同的 名稱將返回對同一個記錄器對象 的引用。

0

我對Python的全局變量是如何工作感到困惑。在一個函數中,只需要聲明global logger,如果您正在執行類似logger = logging.getLogger("my logger")並希望修改全球logger

因此,要修改您的示例,您可以在文件的開頭創建一個全局記錄器對象。如果您的模塊可以由另一個模塊導入,則應該添加NullHandler,這樣如果庫的導入器不需要啓用日誌記錄,那麼它們與您的lib沒有任何問題(ref)。

#!/usr/bin/env python 
import logging 
import ... 

logger = logging.getLogger("my logger").addHandler(logging.NullHandler()) 

def configure_logging(): 
    logger.setLevel(logging.DEBUG) 
    # Format for our loglines 
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 
    # Setup console logging 
    ch = logging.StreamHandler() 
    ch.setLevel(logging.DEBUG) 
    ch.setFormatter(formatter) 
    logger.addHandler(ch) 
    # Setup file logging as well 
    fh = logging.FileHandler(LOG_FILENAME) 
    fh.setLevel(logging.DEBUG) 
    fh.setFormatter(formatter) 
    logger.addHandler(fh) 

def count_parrots(): 
    ... 
    logger.debug('counting parrots') 
    ... 
    return parrots 

if __name__ == '__main__': 
    configure_logging() 
    logger.debug("I'm a log file") 
    parrots = count_parrots()