2013-10-27 39 views
26

是否可以在python中使用fileConfig更改日誌級別?如果通過fileConfig無法實現,還有其他方法可以獲得相同的結果嗎?在不重新啓動應用程序的情況下動態更改python中的日誌級別

更新:這是在服務器上運行的應用程序,我想系統管理者能夠改變,將在運行時由應用程序選擇了一個配置文件,並動態地修改日誌級別。那時我正在與gevent合作,因此我已經將我的代碼添加爲使用inotify挑選配置文件更改的答案之一。

+1

大家重置日誌記錄級別。這是發明的信號。您可以使用「SIGHUP」(這是* nix上的標準)來指示進程以重新加載配置。在python中,你可以安裝一個信號處理程序來捕捉這個(事件驅動器),從而重新加載或重置一個配置。 –

+0

我同意使用信號。建議使用SIGUSR1或SIGUSR2來代替SIGHUP。後者在流程控制終端斷開連接時發送。 –

回答

47

fileConfig是配置基於文件爲你的日誌級別的機制;您可以隨時在程序中動態更改它。您要更改日誌級別的日誌對象上

呼叫.setLevel()。通常你想這麼做根:

logging.getLogger().setLevel(logging.DEBUG) 
+0

另請參見@sfinkens(以及我的評論)的答案,以防您只想更改特定處理程序的setLevel。 – Starman

2

這可能是你在找什麼:

import logging 
logging.getLogger().setLevel(logging.INFO) 

注意getLogger()稱爲不帶任何參數返回根記錄器。

4

確實有可能使用fileConfig()即時更改日誌配置,但對於簡單的更改,Martijn Pieters的答案中提出的程序化方法可能是適當的。日誌記錄甚至提供一個套接字服務器來監聽使用listen()/stopListening() API的配置更改,如記錄here。若要記錄監聽某個端口,可以使用

t = logging.config.listen(PORT_NUMBER) 
t.start() 

,並停止監聽,通話

logging.config.stopListening() 

將數據發送到服務器,可以使用例如

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(('localhost', PORT_NUMBER)) 
with open(CONFIG_FILE) as f: 
    data_to_send = f.read() 
s.send(struct.pack('>L', len(data_to_send))) 
s.send(data_to_send) 
s.close() 

更新:由於向後兼容性的限制,內部實行的fileConfig()的通話意味着你不能在通話,這使得在某些情況下不太有用此功能指定disable_existing_loggers=False。您可以使用相同的API使用dictConfig模式發送JSON文件,這樣可以更好地控制重新配置。這需要Python 2.7/3.2或更高版本(其中添加了dictConfig())。或者,您可以使用stdlib代碼來實現您自己的偵聽器,該偵聽器以相同的方式工作,但是它是針對您的特定需求量身定製的。

+1

哇,我今天早些時候看到這個答案,但沒有注意到更新。我正在爲這個問題而努力。有一個錯誤提交添加該選項。 http://bugs.python.org/issue26533 現在我在一個祕密的方式解決這個問題了: – Gdogg

+0

'''蟒蛇 高清my_fileConfig(FNAME,默認值=無,disable_existing_loggers = FALSE): 「」」 這並不_NOTHING_但撥打fileConfig與disable_existing_loggers設置 爲False,而不是真實的。它的目的是猴子patcn出來,使 配置監聽器可以在不禁止所有非根記錄器使用。 「」」 orig_fileConfig( fname,defaults,disable_existing_loggers) orig_fileConfig = logging.config.fileConfig #HACK:Patch out fil eConfig爲我的版本。 logging.config.fileConfig = my_fileConfig logging.config.fileConfig(config_path) ''' – Gdogg

0

根據您的應用程序,你首先需要找到重新加載該文件或復位以執行過程中自己的配置文件的日誌級別的方法。

最簡單的方法是使用一個計時器。要麼使用線程來做到這一點,要麼讓你的異步框架來做到這一點(如果你使用任何;他們通常實現它)。

使用線程。計時器:

import threading 
import time 


def reset_level(): 
    # you can reload your own config file or use logging.config.fileConfig here 
    print 'Something else' 
    pass 


t = threading.Timer(10, reset_level) 
t.start() 

while True: 
    # your app code 
    print 'Test' 
    time.sleep(2) 

輸出:

Test 
Test 
Test 
Test 
Test 
Something else 
Test 
Test 

更新: 請檢查通過的Martijn Pieters的提出的解決方案。

+0

我不認爲這完全回答了這個問題。 –

+0

我已經使用了上述解決方案和我面臨的問題(在rhel5中使用python 2.4.3並使用ConcurrentLogHandler),對於配置文件的每次重新加載,它都會添加一個文件句柄,並在運行一段時間後引發「太多打開的文件」例外。所以我同意特拉維斯熊,Martijn彼得斯的親屬應該是公認的。 – Fabian76

2

我終於與使用inotify和gevent來檢查文件寫入操作,一旦我知道文件已被更改,然後我去爲每個記錄器設置基於配置的級別。

import gevent 
import gevent_inotifyx as inotify 
from gevent.queue import Queue 

class FileChangeEventProducer(gevent.Greenlet): 
    def __init__(self, fd, queue): 
     gevent.Greenlet.__init__(self) 
     self.fd = fd 
     self.queue = queue 

    def _run(self): 
     while True: 
      events = inotify.get_events(self.fd) 
      for event in events: 
       self.queue.put(event) 
       gevent.sleep(0) 


class FileChangeEventConsumer(gevent.Greenlet): 
    def __init__(self, queue, callBack): 
     gevent.Greenlet.__init__(self) 
     self.queue = queue 
     self.callback = callBack 

    def _run(self): 
     while True: 
      _ = self.queue.get() 
      self.callback() 
      gevent.sleep(0) 


class GeventManagedFileChangeNotifier: 
    def __init__(self, fileLocation, callBack): 
     self.fileLocation = fileLocation 
     self.callBack = callBack 
     self.queue = Queue() 
     self.fd = inotify.init() 
     self.wd = inotify.add_watch(self.fd, self.fileLocation, inotify.IN_CLOSE_WRITE) 


    def start(self): 
     producer = FileChangeEventProducer(self.fd, self.queue) 
     producer.start() 
     consumer = FileChangeEventConsumer(self.queue, self.callBack) 
     consumer.start() 
     return (producer, consumer) 

上面的代碼被使用像下面,

def _setUpLoggingConfigFileChangeNotifier(self): 
     loggingFileNameWithFullPath = self._getFullPathForLoggingConfig() 
     self.gFsNotifier = GeventManagedFileChangeNotifier(loggingFileNameWithFullPath, self._onLogConfigChanged) 
     self.fsEventProducer, self.fsEventConsumer = self.gFsNotifier.start() 


    def _onLogConfigChanged(self): 
     self.rootLogger.info('Log file config has changed - examining the changes') 
     newLoggingConfig = Config(self.resourcesDirectory, [self.loggingConfigFileName]).config.get('LOG') 
     self.logHandler.onLoggingConfigChanged(newLoggingConfig) 

一旦我有了新的日誌文件的配置,我可以在正確的日誌記錄級別從配置每個記錄器導線。我只是想分享答案,如果他們試圖用gevent來使用它,它可能會有幫助。

3

除了公認的答案:根據您如何初始化的記錄,你可能還需要更新記錄的處理程序:使用線程和定時器

import logging 

level = logging.DEBUG 
logger = logging.getLogger() 
logger.setLevel(level) 
for handler in logger.handlers: 
    handler.setLevel(level) 
+0

有用的:如果你使用dictConfig來指定你的Logger,Handlers等,並且你只想在一個特定的Handler上設置Level,你可以在這個循環中使用.get_name()函數,如@sfinkens所示,確保你只能改變你想要的水平。 – Starman

相關問題