Ist es möglich, die Protokollebene mit fileConfig in Python zu ändern, ohne die Anwendung neu zu starten? Wenn dies nicht über fileConfig erreicht werden kann, gibt es eine andere Möglichkeit, das gleiche Ergebnis zu erzielen?
Update: Dies war für eine Anwendung, die auf einem Server ausgeführt wird. Ich wollte, dass Systemadministratoren eine Konfigurationsdatei ändern können, die zur Laufzeit von der Anwendung ausgewählt wird, und die Protokollstufe dynamisch ändern. Ich habe zu dieser Zeit mit gevent gearbeitet, daher habe ich meinen Code als eine der Antworten hinzugefügt, die inotify verwendet, um Änderungen an der Konfigurationsdatei auszuwählen.
SIGUSR1
,SIGUSR2
sind nur für Unix-ähnliche Systeme verfügbar. Sie können verwendenlogging.config.listen()
, um nach neuen Konfigurationen zu suchen ( docs.python.org/3/library/… )Antworten:
fileConfig
ist ein Mechanismus zum Konfigurieren der Protokollebene für Sie basierend auf einer Datei. Sie können es jederzeit in Ihrem Programm dynamisch ändern.Rufen Sie
.setLevel()
das Protokollierungsobjekt auf, für das Sie die Protokollstufe ändern möchten. Normalerweise würden Sie das auf der Wurzel tun:quelle
Zusätzlich zur akzeptierten Antwort: Abhängig davon, wie Sie den Logger initialisiert haben, müssen Sie möglicherweise 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)
quelle
Wenn Sie die Antwort von sfinken und den nachfolgenden Kommentar von Starman erweitern, können Sie auch den Typ des Handlers überprüfen, der auf einen bestimmten Outputter abzielt - zum Beispiel:
import logging logger = logging.getLogger() for handler in logger.handlers: if isinstance(handler, type(logging.StreamHandler())): handler.setLevel(logging.DEBUG) logger.debug('Debug logging enabled')
quelle
Es ist sicherlich möglich, die
fileConfig()
Protokollierungskonfiguration im laufenden Betrieb zu ändern, obwohl für einfache Änderungen ein programmatischer Ansatz geeignet sein könnte, wie in der Antwort von Martijn Pieters vorgeschlagen. Die Protokollierung bietet sogar einen Socket-Server, der mithilfe derlisten()
/stopListening()
APIs auf Konfigurationsänderungen wartet, wie hier dokumentiert . Sie verwenden die Protokollierung, um einen bestimmten Port abzuhörenund um nicht mehr zuzuhören, rufen Sie an
Um Daten an den Server zu senden, können Sie z
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()
Aktualisieren: Aufgrund von Abwärtskompatibilitätsbeschränkungen
fileConfig()
bedeutet die interne Implementierung des Aufrufs, dass Siedisable_existing_loggers=False
im Aufruf keine Angaben machen können , wodurch diese Funktion in bestimmten Szenarien weniger nützlich ist. Sie können dieselbe API verwenden, um eine JSON-Datei mithilfe des dictConfig-Schemas zu senden, wodurch eine bessere Kontrolle über die Neukonfiguration ermöglicht wird. Dies erfordert Python 2.7 / 3.2 oder höher (wodictConfig()
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 spezifischen Anforderungen zugeschnitten ist.quelle
python def my_fileConfig(fname, defaults=None, disable_existing_loggers=False): """ This does _NOTHING_ but call fileConfig with disable_existing_loggers set to False instead of true. It is intended to monkey patcn it out, so that the config listener can be used without disabling all non-root loggers. """ orig_fileConfig(fname, defaults, disable_existing_loggers) orig_fileConfig = logging.config.fileConfig # HACK: Patch out fileConfig for my version. logging.config.fileConfig = my_fileConfig logging.config.fileConfig(config_path)
Dies könnte das sein, wonach Sie suchen:
import logging logging.getLogger().setLevel(logging.INFO)
Beachten Sie, dass
getLogger()
aufgerufen ohne Argumente den Root-Logger zurückgibt.quelle
Ich habe mich schließlich dafür entschieden, mit inotify und gevent nach dem Dateischreibvorgang zu suchen. Sobald ich weiß, dass die Datei geändert wurde, lege ich die Ebene für jeden Logger fest, den ich basierend auf der Konfiguration 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 wird wie folgt verwendet:
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 Protokolldateikonfiguration habe, kann ich die richtige Protokollierungsstufe für jeden Protokollierer von config verkabeln. Ich wollte nur die Antwort teilen und es könnte jemandem helfen, wenn er versucht, sie mit gevent zu verwenden.
quelle
Abhängig von Ihrer App müssen Sie zuerst einen Weg finden, um diese Datei neu zu laden oder die Protokollebene basierend auf Ihrer eigenen Konfigurationsdatei während der Ausführung zurückzusetzen.
Am einfachsten wäre es, einen Timer zu verwenden. Verwenden Sie dazu entweder Threading oder lassen Sie Ihr Async-Framework dies tun (falls Sie eines verwenden; es wird normalerweise implementiert).
Threading verwenden.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)
Ausgabe:
Test Test Test Test Test Something else Test Test
Update: Bitte überprüfen Sie die von Martijn Pieters vorgeschlagene Lösung.
quelle