2013-10-27 6 views
26

Ist es möglich, die Protokollierungsstufe mit FileConfig in Python ändern, ohne die Anwendung neu zu starten. Wenn es nicht über fileConfig erreicht werden kann, gibt es eine andere Möglichkeit, das gleiche Ergebnis zu erhalten?Dynamisch ändern Protokollstufe in Python ohne Neustart der Anwendung

Update: Dies war für eine Anwendung auf einem Server ausgeführt, ich wollte sys Admins in der Lage sein, eine Konfigurationsdatei zu ändern, die während der Laufzeit von Anwendung ausgewählt werden und die Protokollstufe dynamisch ändern. Ich arbeitete zu der Zeit mit gevent und daher habe ich meinen Code als eine der Antworten hinzugefügt, die inotify verwendet, um Änderungen in der Konfigurationsdatei auszuwählen.

+1

Jeder Threads und Timer mit der Protokollierungsstufe zurückgesetzt werden. Dafür wurden Signale erfunden. Sie können einen Prozess mit "SIGHUP" (der Standard für * nix) signalisieren, um die Konfiguration neu zu laden. In Python können Sie einen Signal-Handler installieren, der diesen (Ereignis-Laufwerk) fängt und somit eine Konfiguration neu lädt oder zurücksetzt. –

+0

Ich stimme der Verwendung von Signalen zu. Verwenden Sie SIGUSR1 oder SIGUSR2 zu diesem Zweck anstelle von SIGHUP. Letzteres wird gesendet, wenn das steuernde Terminal eines Prozesses getrennt wird. –

Antwort

47

fileConfig ist ein Mechanismus zum Konfigurieren der Log-Level für Sie basierend auf einer Datei; Sie können es jederzeit dynamisch in Ihrem Programm ändern.

Rufen Sie .setLevel() für das Protokollierungsobjekt auf, für das Sie die Protokollierungsstufe ändern möchten. Normalerweise würden Sie tun, dass auf der Wurzel:

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

Siehe auch die Antwort von @sfinkens (und meinen Kommentar dort) für den Fall, dass Sie nur den setLevel für einen bestimmten Handler ändern wollen. – Starman

2

Dies könnte das sein, was Sie suchen:

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

Beachten Sie, dass getLogger() genannt ohne Argumente gibt den Root-Logger.

4

Es ist sicherlich möglich, fileConfig() zu verwenden, um Protokollierungskonfiguration im laufenden Betrieb zu ändern, obwohl für einfache Änderungen ein programmatischer Ansatz, wie in Martijn Pieters Antwort vorgeschlagen, angemessen sein könnte. Die Protokollierung bietet sogar einen Socket-Server, der die Konfigurationsänderungen unter Verwendung der listen()/stopListening() APIs überwacht, wie in der Dokumentation here dokumentiert. Zu erhalten Anmeldung an einem bestimmten Port zu hören, verwenden Sie

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

und hören zu stoppen, rufen

logging.config.stopListening() 

Um Daten an den Server zu senden, können Sie zum Beispiel

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() 

Update: Aufgrund der Abwärtskompatibilität Zwänge, die interne Implementierung der fileConfig() Anruf bedeutet, dass Sie nicht disable_existing_loggers=False im Aufruf angeben können, die diese Funktion weniger nützlich in bestimmten Szenarien macht. Sie können die gleiche API verwenden, um eine JSON-Datei mithilfe des dictConfig-Schemas zu senden, was eine bessere Kontrolle über die Neukonfiguration ermöglicht. Dies erfordert Python 2.7/3.2 oder höher (wobei dictConfig() hinzugefügt wurde). Oder Sie können den stdlib-Code verwenden, um Ihren eigenen Listener zu implementieren, der auf die gleiche Weise funktioniert, aber auf Ihre speziellen Anforderungen zugeschnitten ist.

+1

Wow, ich habe diese Antwort heute früh gesehen, aber das Update nicht bemerkt. Ich habe mit genau diesem Problem gekämpft. Es gibt einen Fehler, um die Option hinzuzufügen. http://bugs.python.org/issue26533 Jetzt habe ich, um das Problem in einer hinterhältigen Weise: – Gdogg

+0

'' 'python def my_fileConfig (fname, defaults = None, disable_existing_loggers = False): ""“ Dies gilt _nothing_ aber rufen fileConfig mit disable_existing_loggers gesetzt auf false statt wahr. es zu Affen patcn es heraus vorgesehen ist, so dass die Config-Hörer ohne Abschaltung aller nicht-root-Logger verwendet werden kann. „““ orig_fileConfig (fname, defaults, disable_existing_loggers) orig_fileConfig = Protokollierung.config.fileConfig # HACK: Auspatch fil eConfig für meine Version. logging.config.fileConfig = my_fileConfig logging.config.fileConfig (config_path) '' ' – Gdogg

0

Je nach App müssen Sie zuerst eine Möglichkeit finden, diese Datei neu zu laden oder die Protokollierungsstufe auf der Basis Ihrer eigenen Konfigurationsdatei während der Ausführung zurückzusetzen.

Am einfachsten wäre es, einen Timer zu verwenden. Verwenden Sie dazu Threading, oder stellen Sie Ihr asynchrones Framework dazu her (wenn Sie eines verwenden, implementieren sie es normalerweise).

Verwenden von Threading.Timer:

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) 

Ausgang:

Test 
Test 
Test 
Test 
Test 
Something else 
Test 
Test 

Update: Bitte überprüfen Sie die Lösung von Martijn Pieters vorgeschlagen.

+0

Ich glaube nicht, dass dies die Frage vollständig beantwortet. –

+0

Ich habe die obige Lösung verwendet und das Problem, dem ich gegenüber stehe (auf rhel5 mit Python 2.4.3 und mit ConcurrentLogHandler) ist, dass für jedes Neuladen der Konfigurationsdatei, es ein Dateihandle hinzufügt und nach dem Ausführen für eine Weile es wirft eine Ausnahme "zu viele offene Dateien". Daher stimme ich Travis Bear zu, dass der Anwser von Martijn Pieters der akzeptierte sein sollte. – Fabian76

2

Ich entschied mich schließlich mit inotify und gevent, um nach der Dateischreiboperation zu suchen, und sobald ich weiß, dass die Datei geändert wurde, gehe ich und lege die Ebene für jeden Logger fest, den ich auf der Konfiguration basiert habe.

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) 

Der obige Code wie unten verwendet wird,

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) 

Sobald ich die neue Protokolldatei Config habe ich in der richtigen Protokollierungsebene für jeden Logger von Config-Draht kann. Ich wollte nur die Antwort teilen und es könnte jemand helfen, wenn sie versuchen, es mit Gevent zu verwenden.

3

Neben der akzeptierte Antwort: Je nachdem, wie Sie den Logger initialisiert, müssen Sie unter Umständen auch die Handler des Loggers aktualisieren:

import logging 

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

Nützlich: Wenn Sie eine dictConfig verwendet haben, um Ihre Logger, Handler usw. anzugeben, und Sie möchten nur Level auf einen bestimmten Handler setzen, können Sie die Funktion .get_name() in einer solchen Schleife verwenden, wie @sfinkens hier zeigt, und vergewissere dich, dass du nur das Level des gewünschten änderst. – Starman

Verwandte Themen