2011-08-24 147 views
50

我正在使用Python日誌記錄器。下面是我的代碼:使用Python日誌記錄模塊時的重複日誌輸出

import os 
import time 
import datetime 
import logging 
class Logger : 
    def myLogger(self): 
     logger = logging.getLogger('ProvisioningPython') 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     return logger 

我的問題是,我得到了每個logger.info呼叫日誌文件的多個條目。我該如何解決這個問題?

+0

爲我工作。 Python 3.2和Windows XP。 – Zuljin

+1

你確定你沒有創建多個記錄器實例嗎? – Gandi

+0

是的。在不同的文件中,我正在像Java項目中那樣採用新實例。請指明我是否會造成問題。 – user865438

回答

54

logging.getLogger()已經是單身。 (Documentation

問題在於,每次調用myLogger()時,都會向實例添加另一個處理程序,從而導致重複的日誌。

也許這樣的事情?

import os 
import time 
import datetime 
import logging 

loggers = {} 

def myLogger(name): 
    global loggers 

    if loggers.get(name): 
     return loggers.get(name) 
    else: 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler = logging.FileHandler(
      '/root/credentials/Logs/ProvisioningPython' 
      + now.strftime("%Y-%m-%d") 
      + '.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     loggers.update(dict(name=logger)) 

     return logger 
+3

我想你應該有loggers.update(dict((name,logger)))。 – acrophobia

8

您不止一次致電Logger.myLogger()。將記錄器實例存儲在某個地方並重新使用

另外請注意,如果您在添加任何處理程序之前登錄,將會創建一個默認的StreamHandler(sys.stderr)

+0

其實我試圖訪問記錄器實例,因爲我們在java.But中使用我不知道是否需要爲整個項目只創建一次實例。 – user865438

+1

@ user865483:只需一次。所有標準圖書館記錄器都是單身人士。 –

3

你的記錄器應該像單身一樣工作。您不應該多次創建它。 下面是例子如何看起來:

import os 
import time 
import datetime 
import logging 
class Logger : 
    logger = None 
    def myLogger(self): 
     if None == self.logger: 
      self.logger=logging.getLogger('ProvisioningPython') 
      self.logger.setLevel(logging.DEBUG) 
      now = datetime.datetime.now() 
      handler=logging.FileHandler('ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
      formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
      handler.setFormatter(formatter) 
      self.logger.addHandler(handler) 
     return self.logger 

s = Logger() 
m = s.myLogger() 
m2 = s.myLogger() 
m.info("Info1") 
m2.info("info2") 
+0

然後如果我要在不同的文件中採用不同的實例。假設在文件1中s = Logger() m = s.myLogger() 和文件2中s = Logger()它將起作用或不起作用 m2 = s。myLogger() – user865438

+0

仍然我幾次獲取同一個日誌的副本。我在這裏懷疑是否內部線程日誌打印多個或不。請幫助我。 – user865438

+1

@ user865438,我們不需要擔心實現一個單例(它已經是)。要登錄子模塊,請遵循正式的Logging Cookbook [鏈接](https://docs.python.org/2/howto/logging-cookbook.html#logging-cookbook)。 基本上,您需要遵循命名層次結構,同時命名記錄器,並負責其餘部分。 – narayan

30
import datetime 
import logging 
class Logger : 
    def myLogger(self): 
     logger=logging.getLogger('ProvisioningPython') 
     if not len(logger.handlers): 
      logger.setLevel(logging.DEBUG) 
      now = datetime.datetime.now() 
      handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
      formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
      handler.setFormatter(formatter) 
      logger.addHandler(handler) 
     return logger 

使用Python 2.7

+2

用於logger.handlers長度檢查。 – kakyo

+1

即使當模塊重新加載時(這不是其他答案的情況),這也可以工作 – yco

+1

感謝您的提示,順便檢查列表是否爲空,您不需要使用「len」運算符直接使用如果my_list:.. – redobot

1

記錄儀的實施已經是單發的把戲對我來說

到logging.getLogger( 'someLogger')的多次調用返回一個參考 到相同的記錄器對象。這不僅在同一個 模塊中是正確的,而且在模塊中也是如此,只要它在同一個Python 解釋器進程中。對同一個對象的引用是真實的; 另外,應用程序代碼可以在一個模塊中定義和配置一個父代 記錄器,並在 一個單獨的模塊中創建(但不配置)子記錄器,並且所有對子代的記錄器調用將通過父代的 。這裏是一個主模塊

源 - Using logging in multiple modules

所以,你應該利用這個方法是 -

假設我們已經創建並配置一個叫做主模塊中「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 
... 

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

請參閱this問題稍微詳細的答案。

+1

getLogger似乎爲我工作後重新定義處理程序: 'logger = logging.getLogger('my_logger'); logger.handlers = [logger.handlers [0],]' – radtek

0

當您通過importlib.reload重新加載您的模塊時(出於與接受的答案中所解釋的相同的原因),也可能發生雙倍(或三倍或..-基於重新加載次數)的記錄器輸出。我將這個答案僅僅作爲未來的參考,因爲我花了一段時間才弄清楚爲什麼我的輸出是dupli(三重)。

0

一個簡單的解決方法就是像

logger.handlers[:] = [handler] 

這樣你避免追加新的處理程序的基礎列表「處理程序」。

0
from logging.handlers import RotatingFileHandler 
import logging 
import datetime 

# stores all the existing loggers 
loggers = {} 

def get_logger(name): 

    # if a logger exists, return that logger, else create a new one 
    global loggers 
    if name in loggers.keys(): 
     return loggers[name] 
    else: 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler = logging.FileHandler(
      'path_of_your_log_file' 
      + now.strftime("%Y-%m-%d") 
      + '.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     loggers.update(dict(name=logger)) 
     return logger 
+0

請添加說明以使此答案對長期使用更有價值。 –

0

您能得到特定的記錄全部處理程序的列表,所以你可以做這樣的事情

logger = logging.getLogger(logger_name) 
handler_installed = False 
for handler in logger: 
    # Here your condition to check for handler presence 
    if isinstance(handler, logging.FileHandler) and handler.baseFilename == log_filename: 
     handler_installed = True 
     break 

if not handler_installed: 
    logger.addHandler(your_handler) 

在上面的例子中,我們檢查,如果指定一個文件處理程序已經掛鉤到記錄器,但有權訪問所有處理程序的列表,使您能夠決定應添加其他處理程序的標準。

0

今天有這個問題。由於我的函數是@staticmethod,所以上述建議已用random()解決。

看起來像:

import random 

logger = logging.getLogger('ProvisioningPython.{}'.format(random.random())) 
8

因爲Python 3.2你可以檢查處理程序已經存在,如果是這樣,添加新的處理程序之前清除它們。調試時,這是非常方便的代碼包括你的記錄器初始化

if (logger.hasHandlers()): 
    logger.handlers.clear() 

logger.addHandler(handler) 
+0

好的答案,Thx :)) –

0

這是一個除了@ rm957377的回答但爲什麼發生這種情況的解釋。當您在AWS中運行一個lambda函數時,它們將從一個包含多個調用的活動實例中調用您的函數。意思是,如果在函數的代碼中調用addHandler(),則每次函數運行時它都會繼續向記錄單例添加重複的處理程序。 通過多次調用lambda函數,記錄單例持續存在。

爲了解決這個問題,你可以清除處理您通過設置在他們面前:

logging.getLogger().handlers.clear() 
logging.getLogger().addHandler(...)