Est-il possible de changer le niveau de log en utilisant fileConfig en python sans redémarrer l'application. Si cela ne peut pas être réalisé via fileConfig, existe-t-il un autre moyen d'obtenir le même résultat?
Mise à jour: il s'agissait d'une application s'exécutant sur un serveur. Je souhaitais que les administrateurs système puissent modifier un fichier de configuration qui serait sélectionné au moment de l'exécution par l'application et modifier le niveau de journalisation de manière dynamique. À l'époque, je travaillais avec gevent, c'est pourquoi j'ai ajouté mon code parmi les réponses qui utilisent inotify pour choisir les modifications apportées au fichier de configuration.
fileConfig
est un mécanisme permettant de configurer le niveau de journal pour vous en fonction d'un fichier; vous pouvez le changer dynamiquement à tout moment dans votre programme.
Appelez .setLevel()
sur l'objet de journalisation pour lequel vous souhaitez modifier le niveau de journalisation. Habituellement, vous le feriez à la racine:
logging.getLogger().setLevel(logging.DEBUG)
En plus de la réponse acceptée: Selon la manière dont vous avez initialisé le consignateur, vous devrez peut-être également mettre à jour les gestionnaires de ce dernier:
import logging
level = logging.DEBUG
logger = logging.getLogger()
logger.setLevel(level)
for handler in logger.handlers:
handler.setLevel(level)
Il est certainement possible d'utiliser fileConfig()
pour modifier la configuration de la journalisation à la volée, bien que, pour de simples modifications, une approche programmatique telle que suggérée dans la réponse de Martijn Pieters puisse convenir. La journalisation fournit même un serveur de socket pour écouter les modifications de configuration à l'aide des API listen()
stopListening()
, comme documenté ici . Pour que la journalisation soit à l'écoute sur un port particulier, vous utilisez
t = logging.config.listen(PORT_NUMBER)
t.start()
et pour arrêter d'écouter, appelez
logging.config.stopListening()
Pour envoyer des données au serveur, vous pouvez utiliser par exemple.
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: En raison de contraintes de compatibilité ascendante, l'implémentation interne de l'appel fileConfig()
signifie que vous ne pouvez pas spécifier disable_existing_loggers=False
dans l'appel, ce qui rend cette fonctionnalité moins utile dans certains scénarios. Vous pouvez utiliser la même API pour envoyer un fichier JSON à l'aide du schéma dictConfig, ce qui permettra un meilleur contrôle de la reconfiguration. Cela nécessite Python 2.7/3.2 ou supérieur (où dictConfig()
a été ajouté). Ou bien, vous pouvez utiliser le code stdlib pour implémenter votre propre écouteur, qui fonctionne de la même manière mais qui est adapté à vos besoins spécifiques.
C'est peut-être ce que vous recherchez:
import logging
logging.getLogger().setLevel(logging.INFO)
Notez que getLogger()
appelé sans aucun argument renvoie le consignateur racine.
J'ai finalement décidé d'utiliser inotify et gevent pour vérifier l'opération d'écriture de fichier, et une fois que je sais que le fichier a été modifié, je règle le niveau de chaque enregistreur basé sur la configuration.
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)
Le code ci-dessus s'utilise comme ci-dessous,
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)
Une fois que j'ai le nouveau fichier journal config, je peux câbler le bon niveau de journalisation pour chaque enregistreur de config. Je voulais juste partager la réponse et cela pourrait aider quelqu'un s'il essaye de l'utiliser avec gevent.
Selon votre application, vous devez d'abord trouver un moyen de recharger ce fichier ou de réinitialiser le niveau de journalisation en fonction de votre propre fichier de configuration lors de l'exécution.
Le moyen le plus simple serait d'utiliser une minuterie. Soit vous utilisez des threads pour le faire, soit vous vous en servez dans votre framework async (si vous en utilisez un, ils le mettent généralement en œuvre).
Utilisation de 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)
Sortie:
Test
Test
Test
Test
Test
Something else
Test
Test
Mise à jour: Veuillez vérifier la solution proposée par Martijn Pieters.