2011-06-13 154 views
34

我有一段代碼正在初始化一個記錄器,如下所示。Python日誌記錄確保處理程序只添加一次

logger = logging.getLogger() 
hdlr = logging.FileHandler('logfile.log') 
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
hdlr.setFormatter(formatter) 
logger.addHandler(hdlr) 
logger.setLevel(logging.DEBUG) 

遺憾的是這段代碼被稱爲多次,有沒有什麼辦法,我可以檢查,看看是否處理程序已經存在 - 我更願意實現這個,而無需使用單例。

編輯:對不起,忘了提,這是蟒蛇2.5 - 歡呼聲中,理查德

+0

我想你應該重新評估的答案,因爲mouad的回答(在寫作時)忽略了一個事實的方法,多次調用返回相同的記錄器對象* CAN *添加重複處理。納拉揚做了很好的解釋。 – 2016-02-14 08:11:48

回答

21

作爲@offbyone註釋,可以將冗餘處理程序添加到記錄器的同一個實例。 的python docs for logging聲言如下─

「多給getLogger()具有相同的名稱調用將返回一個 參照相同記錄的對象。」

所以我們不需要擔心使實現成爲單例,因爲它已經是了。

不幸的是,同樣是不是真的爲與相同的記錄器實例相關的處理程序。有可以是附加的重複處理程序。

例 -

  1. 複製該代碼,並將其保存在main.py

    import logging 
    print 'inside main.py', 
    print '-'*50 
    def logger(): 
    
         print 'initializing logger....' 
         logPath = '.' 
         fileName = 'temp' 
    
         # configure log formatter 
         logFormatter = logging.Formatter("%(asctime)s [%(filename)s] [%(funcName)s] [%(levelname)s] [%(lineno)d] %(message)s") 
    
         # configure file handler 
         fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName)) 
         fileHandler.setFormatter(logFormatter) 
    
         # configure stream handler 
         consoleHandler = logging.StreamHandler() 
         consoleHandler.setFormatter(logFormatter) 
    
         # get the logger instance 
         logger = logging.getLogger(__name__) 
    
         # set the logging level 
         logger.setLevel(logging.DEBUG) 
    
         print 'adding handlers- ' 
    
         #if not len(logger.handlers): 
         logger.addHandler(fileHandler) 
         logger.addHandler(consoleHandler) 
    
         print 'logger initialized....\n' 
         print 'associated handlers - ', len(logger.handlers) 
         for handler in logger.handlers: 
          print handler 
         print 
         return logger 
    
    main_logger = logger() 
    main_logger.info('utilizing main.py logger.') 
    print 'exiting main.py', 
    print '-'*50 
    
  2. 和下面的代碼sub.py

    print 'inside sub.py', 
    print '-'*50 
    print 'importing main.py' 
    import main 
    print 'imported main.py' 
    import logging 
    print 'getting logger instance in sub' 
    sub_logger = main.logger() 
    print 'got logger instance in sub' 
    sub_logger.info("utilizing sub_logger") 
    print 'exiting sub.py', 
    print '-'*50 
    
  3. 跑分。該方法的py

    [email protected]:~/code/so$ python sub.py 
    inside sub.py -------------------------------------------------- 
    importing main.py 
    inside main.py -------------------------------------------------- 
    initializing logger.... 
    adding handlers- 
    logger initialized.... 
    
    associated handlers - 2 
    <logging.FileHandler object at 0x7f7158740c90> 
    <logging.StreamHandler object at 0x7f7158710b10> 
    
    2015-08-04 07:41:01,824 [main.py] [<module>] [INFO] [41] utilizing main.py logger. 
    exiting main.py -------------------------------------------------- 
    imported main.py 
    getting logger instance in sub 
    initializing logger.... 
    adding handlers- 
    logger initialized.... 
    
    associated handlers - 4 # <===== 4 handlers (duplicates added) 
    <logging.FileHandler object at 0x7f7158740c90> 
    <logging.StreamHandler object at 0x7f7158710b10> 
    <logging.FileHandler object at 0x7f7158710bd0> 
    <logging.StreamHandler object at 0x7f7158710c10> 
    
    got logger instance in sub 
    2015-08-04 07:41:01,824 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
    2015-08-04 07:41:01,824 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
    exiting sub.py -------------------------------------------------- 
    

因此多個呼叫返回相同的記錄器添加重複處理程序。現在

,爲您question-

有什麼辦法,我可以檢查,看看是否處理程序已經存在

是的,有是 -

logger.handlers返回列表所有與給定logger相關聯的處理程序。

之前將處理程序記錄器的實例,確保不添加重複處理 在main.py,只是取消註釋,上面寫着if not len(logger.handlers):行和縮進下面兩行properly-

if not len(logger.handlers): 
    logger.addHandler(fileHandler) 
    logger.addHandler(consoleHandler) 

現在再次運行sub.py

[email protected]:~/code/so$ python sub.py 
inside sub.py -------------------------------------------------- 
importing main.py 
inside main.py -------------------------------------------------- 
initializing logger.... 
adding handlers- 
logger initialized.... 

associated handlers - 2 
<logging.FileHandler object at 0x7fd67a891c90> 
<logging.StreamHandler object at 0x7fd67a862b10> 

2015-08-04 08:14:45,620 [main.py] [<module>] [INFO] [41] utilizing main.py logger. 
exiting main.py -------------------------------------------------- 
imported main.py 
getting logger instance in sub 
initializing logger.... 
adding handlers- 
logger initialized.... 

associated handlers - 2 # <===== Still 2 handlers (no duplicates) 
<logging.FileHandler object at 0x7fd67a891c90> 
<logging.StreamHandler object at 0x7fd67a862b10> 

got logger instance in sub 
2015-08-04 08:14:45,620 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
exiting sub.py -------------------------------------------------- 

此外,如果你想限制要添加到記錄器實例處理程序的類型,你可以不喜歡這個 -

print 'adding handlers- ' 
    # allows to add only one instance of file handler and stream handler 
    if len(logger.handlers) > 0: 
     print 'making sure we do not add duplicate handlers' 
     for handler in logger.handlers: 
       # add the handlers to the logger 
       # makes sure no duplicate handlers are added 

       if not isinstance(handler, logging.FileHandler) and not isinstance(handler, logging.StreamHandler): 
        logger.addHandler(fileHandler) 
        print 'added file handler' 
        logger.addHandler(consoleHandler) 
        print 'added stream handler' 
    else: 
     logger.addHandler(fileHandler) 
     logger.addHandler(consoleHandler) 
     print 'added handlers for the first time' 

希望這有助於!

編輯:

不幸的是,同樣是不是真的與記錄器的相同實例相關聯 的處理程序。有可以重複 處理程序附加。

事實證明,上述說法並不完全正確。

假設我們已經在主模塊中創建並配置了一個名爲'main_logger'的記錄器(它簡單地配置記錄器,不會返回任何東西)。

# get the logger instance 
logger = logging.getLogger("main_logger") 
# configuration follows 
... 

現在一個子模塊,如果我們創建下列命名層次「main_logger.sub_module_logger」孩子記錄儀,我們不需要配置它的子模塊中。只需在命名層次結構中創建記錄器就足夠了。

# get the logger instance 
logger = logging.getLogger("main_logger.sub_module_logger") 
# no configuration needed 
# it inherits the configuration from the parent logger 
... 

而且它也不會添加重複處理程序。

參考 - Using logging in multiple modules

+1

這絕對是最好的答案。 mouad的答案不幸是短視的,因爲它忽略了單個記錄器實例(單例)的重複處理程序(而不是單例)的可能性。你的回答幫助我解決了我的問題。 – 2016-02-14 08:02:22

+0

「logger.handlers返回與給定記錄器相關的所有處理程序的列表」...是的,但我可以問你,你是如何發現這一點的?你有沒有回到源代碼?因爲python日誌模塊文檔https://docs.python.org/2/library/logging.html沒有顯示這個信息。有時我覺得Python的標準文檔有點缺乏......你可以推薦更好的東西嗎? – 2016-02-20 14:45:06

+0

@mikerodent我在搜索結果中發現了有關_logger.handlers_的問題,當我在日誌記錄中遇到這個重複輸出問題並且在源代碼中驗證了相同的結果時。關於文檔,標準文檔通常會完成這項工作。 [pymotw](https://pymotw.com/2/contents.html)可以幫助我,只要我想要在python標準庫的幫助下完成特定的任務。 – narayan 2016-02-24 17:42:11

14

那麼,如果處理程序已經存在logger.addHandler()不會添加處理程序。要檢查處理器已經在那裏你可以檢查logger.handlers列表:

logger = logging.getLogger() 
hdlr = logging.FileHandler('logfile.log') 
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
hdlr.setFormatter(formatter) 
logger.addHandler(hdlr) 
logger.setLevel(logging.DEBUG) 
print logger.handlers 
# [<logging.FileHandler object at 0x14542d0>] 
logger.addHandler(hdlr) 
print logger.handlers 
# [<logging.FileHandler object at 0x14542d0>] 

除此之外,我會建議把這個代碼在main()函數,如果你有一個或__init__.py文件你的包所以不必每次都調用它。我還建議你使用命名記錄器,並且不要使用根記錄器。事情是這樣的:

logger = logging.getLogger(__name__) 
... 

希望這是有幫助:)

+2

是「Logger.handlers」列表記錄在任何地方?我無法在http:// docs上找到它。python.org/2/library/logging.html – 2013-08-23 16:53:12

+1

@MariuszPluciński:是的,我不認爲它記錄在任何地方,但是如果我記得很好,我必須檢查記錄器類代碼以瞭解如何工作:http:// hg。 python.org/cpython/file/482590320549/Lib/logging/__init__.py#l1215 – mouad 2013-08-26 09:10:05

+1

但是如果它沒有記錄,那麼它可能會在未來版本的日誌庫中悄然消失。我不確定是否可以依賴這個功能(但我同意,似乎沒有任何記錄的方式來檢查此功能)。 – 2013-08-26 17:38:08

1

嘗試檢查,如果logger已經設置。例如,如果此代碼位於某個函數中:

logger = None 
def init_logger(): 
    global logger 
    if logger is not None: 
     #logger has already been initialized 
     return 
    logger = logging.getLogger() 
    hdlr = logging.FileHandler('logfile.log') 
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
    hdlr.setFormatter(formatter) 
    logger.addHandler(hdlr) 
    logger.setLevel(logging.DEBUG) 
+0

我想檢查'如果記錄器是不None'不是線程安全的,所以不能保證初始化代碼將不會運行一次以上。 – 2012-03-27 22:25:49

2

您也可以檢查處理程序列表是否爲空。這裏是我結束了與解決方案:

def setup_logging(self, logfile): 
    self._logger = logging.getLogger('TestSuite') 
    self._logger.setLevel(logging.INFO) 
    host = socket.gethostname().split('.')[0] 
    if self._logger.handlers == []: 
     fh = logging.handlers.RotatingFileHandler(logfile, 
                maxBytes=10*1024*1024, 
                backupCount=5) 
     strfmt = "%" + "(asctime)s [%s] " % host + "%" + "(message)s" 
     fmt = logging.Formatter(strfmt, datefmt="%Y.%m%d %H:%M:%S") 
     fh.setFormatter(fmt) 

     self._logger.addHandler(fh) 
    self._logger.info('-' * (55 - len(host))) 

我所看到的處理程序添加多次,所以每個日誌消息是越來越寫入日誌文件多於一次,這固定它。

相關問題