wake-up-neo.com

Log-Level in Python dynamisch ändern, ohne die Anwendung neu zu starten

Ist es möglich, die Protokollierungsstufe mit fileConfig in Python zu ändern, ohne die Anwendung neu zu starten. Wenn es nicht durch fileConfig erreicht werden kann, gibt es einen anderen Weg, um das gleiche Ergebnis zu erzielen? 

Update: Dies war für eine Anwendung, die auf einem Server ausgeführt wird. Ich wollte, dass sys-Administratoren eine Konfigurationsdatei ändern können, die zur Laufzeit von der Anwendung ausgewählt wird, und die Protokollierungsstufe dynamisch ändern. Ich arbeitete zu diesem Zeitpunkt mit gevent, daher habe ich meinen Code als eine der Antworten hinzugefügt, die inotify verwendet, um Änderungen an der Konfigurationsdatei auszuwählen.

45
opensourcegeek

fileConfig ist ein Mechanismus zum Konfigurieren der Protokollebene 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 Protokollebene ändern möchten. Normalerweise tun Sie das auf der Wurzel:

logging.getLogger().setLevel(logging.DEBUG)
78
Martijn Pieters

Zusätzlich zur akzeptierten Antwort: Je nachdem, 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)
9
sfinkens

Es ist sicherlich möglich, fileConfig() zu verwenden, um die Protokollierungskonfiguration im laufenden Betrieb zu ändern. Für einfache Änderungen kann jedoch ein programmatischer Ansatz, wie er in der Antwort von Martijn Pieters vorgeschlagen wird, angebracht sein. Die Protokollierung bietet sogar einen Socket-Server zum Abhören von Konfigurationsänderungen mithilfe der listen()stopListening()-APIs, wie im Dokument hier beschrieben. Um die Protokollierung zum Abhören eines bestimmten Ports zu erhalten, verwenden Sie

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

und um nicht mehr zu hören, rufen Sie an

logging.config.stopListening()

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

_/Update: Aufgrund von Rückwärtskompatibilitätsbeschränkungen bedeutet die interne Implementierung des Aufrufs fileConfig(), dass Sie im Aufruf nicht disable_existing_loggers=False angeben können. Dies macht diese Funktion in bestimmten Szenarien weniger nützlich. Sie können dieselbe API verwenden, um eine JSON-Datei mit dem Schema dictConfig zu senden, wodurch die Neukonfiguration besser gesteuert werden kann. Dies erfordert Python 2.7/3.2 oder höher (wobei dictConfig() hinzugefügt wurde). Sie können den stdlib-Code auch verwenden, um Ihren eigenen Listener zu implementieren, der auf dieselbe Weise funktioniert, jedoch auf Ihre speziellen Bedürfnisse zugeschnitten ist.

6
Vinay Sajip

Das könnte sein, wonach Sie suchen:

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

Beachten Sie, dass getLogger() ohne Argumente den Root-Logger zurückgibt.

2
Rolando Max

Ich entschied mich schließlich für die Verwendung von inotify und gevent, um nach dem Schreibvorgang für Dateien zu suchen. Sobald ich weiß, dass die Datei geändert wurde, lege ich den Pegel 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 Logdatei config habe, kann ich für jeden Logger die richtige Logging-Ebene von config verbinden. Ich wollte nur die Antwort teilen und es könnte jemandem helfen, wenn sie versuchen, es mit gevent zu verwenden.

2
opensourcegeek

Wenn Sie sfinkens Antwort und Starmans nachfolgenden Kommentar 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')
1
DrOffler

Abhängig von Ihrer App müssen Sie zunächst eine Möglichkeit finden, diese Datei erneut zu laden oder den Protokollierungsgrad 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 machen Sie Ihr async-Framework dazu (wenn Sie eines verwenden, sie implementieren es normalerweise).

Threading.Timer verwenden:

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.

0
Mihai