À l'heure actuelle, j'ai un module central dans un cadre qui génère plusieurs processus à l'aide du multiprocessing
module Python 2.6 . Parce qu'il utilise multiprocessing
, il est au niveau du module journal multitraitement-conscient, LOG = multiprocessing.get_logger()
. Selon la documentation , cet enregistreur a des verrous partagés de processus afin que vous ne perdiez pas les choses sys.stderr
(ou quoi que ce soit) en ayant plusieurs processus écrivant simultanément.
Le problème que j'ai maintenant, c'est que les autres modules du cadre ne sont pas compatibles avec le multitraitement. D'après ce que je vois, je dois faire en sorte que toutes les dépendances de ce module central utilisent une journalisation prenant en charge le multitraitement. C'est ennuyeux dans le cadre, et encore moins pour tous les clients du cadre. Y a-t-il des alternatives auxquelles je ne pense pas?
la source
multiprocessing.get_logger()
? Il semble que, sur la base de ces autres méthodes de journalisation, la fonctionnalité de journalisation aitmultiprocessing
peu de valeur.get_logger()
est l'enregistreur utilisé par lemultiprocessing
module lui-même. C'est utile si vous souhaitez déboguer unmultiprocessing
problème.Réponses:
La seule façon de gérer ce problème de manière non intrusive est de:
select
partir des descripteurs de fichiers des canaux, effectuez un tri par fusion sur les entrées de journal disponibles et effectuez un vidage dans le journal centralisé. Répétez.)la source
atexit
:-). Le problème est qu'il ne vous donnera pas une lecture en temps réel. Cela peut faire partie du prix du multitraitement par opposition au multithreading.multiprocessing.Queue
ne sera pas plus simple s'il y a beaucoup de code à recâbler à utilisermultiprocessing.Queue
et / ou si les performances sont un problèmeJe viens d'écrire mon propre gestionnaire de journaux qui alimente tout simplement le processus parent via un canal. Je ne le teste que depuis dix minutes mais cela semble plutôt bien fonctionner.
( Remarque: ceci est codé en dur
RotatingFileHandler
, ce qui est mon propre cas d'utilisation.)Mise à jour: @javier maintient maintenant cette approche comme un package disponible sur Pypi - voir multiprocessing-logging sur Pypi, github sur https://github.com/jruere/multiprocessing-logging
Mise à jour: mise en œuvre!
Cela utilise désormais une file d'attente pour une gestion correcte de la concurrence, et récupère également les erreurs correctement. Je l'utilise maintenant en production depuis plusieurs mois, et la version actuelle ci-dessous fonctionne sans problème.
la source
multiprocessing.Queue
utilise un thread pour entrerput()
. N'invoquez donc pasput
(c'est-à-dire enregistrez un msg à l'aide d'unMultiProcessingLog
gestionnaire) avant de créer tous les sous-processus. Sinon, le thread sera mort dans le processus enfant. Une solution consiste à appelerQueue._after_fork()
au début de chaque processus enfant, ou à utiliser à lamultiprocessing.queues.SimpleQueue
place, ce qui n'implique pas de thread mais est bloquant.multiprocessing-logging
.QueueHandler
est natif de Python 3.2+, et fait exactement cela. Il est facilement répliqué dans les versions précédentes.Les documents Python ont deux exemples complets: Connexion à un seul fichier à partir de plusieurs processus
Pour ceux qui utilisent Python <3.2, copiez simplement
QueueHandler
dans votre propre code à partir de: https://gist.github.com/vsajip/591589 ou importez également logutils .Chaque processus (y compris le processus parent) place sa journalisation dans
Queue
, puis unlistener
thread ou un processus (un exemple est fourni pour chacun) les récupère et les écrit tous dans un fichier - aucun risque de corruption ou de brouillage.la source
Vous trouverez ci-dessous une autre solution axée sur la simplicité pour quiconque (comme moi) qui arrive ici de Google. L'enregistrement devrait être facile! Seulement pour 3.2 ou supérieur.
la source
QueueHandler
etQueueListener
peuvent également être utilisées sur Python 2.7, disponible dans lelogutils
package.Une autre alternative pourrait être les divers gestionnaires de journalisation non basés sur des fichiers dans le
logging
package :SocketHandler
DatagramHandler
SyslogHandler
(et d'autres)
De cette façon, vous pourriez facilement avoir un démon de journalisation quelque part dans lequel vous pourriez écrire en toute sécurité et gérer correctement les résultats. (Par exemple, un simple serveur de socket qui décompresse simplement le message et le transmet à son propre gestionnaire de fichiers rotatif.)
Ils s'en
SyslogHandler
occuperaient également pour vous. Bien sûr, vous pouvez utiliser votre propre instance desyslog
, pas celle du système.la source
Une variante des autres qui maintient le thread de journalisation et de file d'attente séparé.
la source
fileConfig()
dans MainProcess et un enregistreur à peine configuré dans PoolWorkers (avec seulementsetLevel(logging.NOTSET)
). Comme je l'ai mentionné dans un autre commentaire, j'utilise Pool, j'ai donc dû obtenir ma file d'attente (proxy) auprès de Manager au lieu du multitraitement afin qu'elle puisse être décapée. Cela me permet de passer la file d'attente à un travailleur à l'intérieur d'un dictionnaire (dont la plupart est dérivée de l'objet argsparse utilisantvars()
). J'ai l'impression qu'à la fin c'est la meilleure approche pour MS Windows qui manque de fork () et casse la solution @zzzeak.fork
. De cette façon, chaque processus aura sa propre file d'attente inutile indépendante. La deuxième approche dans le Q / A lié ne fonctionnera pas sur de telles plateformes. C'est un moyen de code non portable.multiprocessing.Queue
avec le processus principal et je l'utilise constamment depuis. Je ne prétends pas comprendre pourquoi cela fonctionne.Toutes les solutions actuelles sont trop couplées à la configuration de journalisation à l'aide d'un gestionnaire. Ma solution présente l'architecture et les fonctionnalités suivantes:
multiprocessing.Queue
logging.Logger
(et les instances déjà définies) sont corrigés pour envoyer tous les enregistrements à la file d'attenteLe code avec l'exemple d'utilisation et la sortie peut être trouvé dans le Gist suivant: https://gist.github.com/schlamar/7003737
la source
daemon_thread.daemon
àTrue
. J'avais besoin de le faire pour que mon programme Python se ferme correctement lorsqu'une exception se produit dans le gestionnaire de contexte.func
enlogged_call
, sinon l'exception obtiendrait brouillée avec une autre sortie registré. Voici ma version modifiée de ceci: gist.github.com/blah238/8ab79c4fe9cdb254f5c37abfc5dc85bfComme nous pouvons représenter la journalisation multiprocessus autant d'éditeurs et d'un abonné (auditeur), l'utilisation de ZeroMQ pour implémenter la messagerie PUB-SUB est en effet une option.
De plus, le module PyZMQ , les liaisons Python pour ZMQ, implémente PUBHandler , qui est un objet pour publier des messages de journalisation sur un socket zmq.PUB.
Il existe une solution sur le Web pour la journalisation centralisée à partir d'applications distribuées à l'aide de PyZMQ et PUBHandler, qui peut être facilement adoptée pour travailler localement avec plusieurs processus de publication.
la source
J'aime aussi la réponse de zzzeek, mais André a raison de dire qu'une file d'attente est nécessaire pour éviter les déformations. J'ai eu de la chance avec la pipe, mais j'ai vu des gargarismes qui sont quelque peu attendus. L'implémenter s'est avéré plus difficile que je ne le pensais, en particulier en raison de l'exécution sous Windows, où il existe des restrictions supplémentaires sur les variables globales et autres (voir: Comment le multiprocessing Python est-il implémenté sous Windows? )
Mais je l'ai finalement fait fonctionner. Cet exemple n'est probablement pas parfait, donc les commentaires et suggestions sont les bienvenus. Il ne prend pas non plus en charge la définition du formateur ou autre chose que l'enregistreur racine. Fondamentalement, vous devez réactiver l'enregistreur dans chacun des processus de pool avec la file d'attente et configurer les autres attributs sur l'enregistreur.
Encore une fois, toutes les suggestions sur la façon d'améliorer le code sont les bienvenues. Je ne connais certainement pas encore toutes les astuces Python :-)
la source
if 'MainProcess' == multiprocessing.current_process().name:
peut être utilisé au lieu de passerchild
?il suffit de publier quelque part votre instance de l'enregistreur. de cette façon, les autres modules et clients peuvent utiliser votre API pour obtenir l'enregistreur sans avoir à le faire
import multiprocessing
.la source
import logging; logging.basicConfig(level=logging.DEBUG); logging.debug('spam!')
de n'importe où et de le faire fonctionner correctement.J'ai aimé la réponse de zzzeek. Je remplacerais simplement le tuyau par une file d'attente, car si plusieurs threads / processus utilisent la même extrémité de tuyau pour générer des messages de journal, ils seront tronqués.
la source
Que diriez-vous de déléguer toute la journalisation à un autre processus qui lit toutes les entrées de journal d'une file d'attente?
Partagez simplement LOG_QUEUE via l'un des mécanismes multiprocessus ou même l'héritage et tout fonctionne bien!
la source
J'ai une solution similaire à celle d'ironhacker, sauf que j'utilise logging.exception dans certains de mes codes et j'ai constaté que je devais formater l'exception avant de la renvoyer sur la file d'attente car les retraits ne sont pas picklables:
la source
Ci-dessous est une classe qui peut être utilisée dans un environnement Windows, nécessite ActivePython. Vous pouvez également hériter d'autres gestionnaires de journalisation (StreamHandler, etc.)
Et voici un exemple qui montre l'utilisation:
la source
multiprocessing.Lock()
au lieu de Windows Mutex rendrait la solution portable.Voici mon hack / solution de contournement simple ... pas le plus complet, mais facilement modifiable et plus simple à lire et à comprendre, je pense que toutes les autres réponses que j'ai trouvées avant d'écrire ceci:
la source
Il y a ce super package
Package: https://pypi.python.org/pypi/multiprocessing-logging/
code: https://github.com/jruere/multiprocessing-logging
Installer:
Puis ajouter:
la source
L'une des alternatives est d'écrire la journalisation du multiprocessing dans un fichier connu et d'enregistrer un
atexit
gestionnaire pour se joindre à ces processus, relisez-le sur stderr; cependant, vous n'obtiendrez pas un flux en temps réel vers les messages de sortie sur stderr de cette façon.la source
Si des blocages se produisent dans une combinaison de verrous, de threads et de fourches dans le
logging
module, cela est signalé dans le rapport de bogue 6721 (voir également la question SO connexe ).Il existe une petite solution de correction publiée ici .
Cependant, cela corrigera simplement tous les blocages potentiels
logging
. Cela ne réglera pas le fait que les choses sont peut-être confuses. Voir les autres réponses présentées ici.la source
Idée la plus simple mentionnée:
[WatchedFileHandler][1]
. Les raisons de ce gestionnaire sont discutées en détail ici , mais en bref, il existe certaines conditions de concurrence plus mauvaises avec les autres gestionnaires de journalisation. Celui-ci a la fenêtre la plus courte pour la condition de course.la source
Pour ceux qui pourraient en avoir besoin, j'ai écrit un décorateur pour le package multiprocessing_logging qui ajoute le nom du processus actuel aux journaux, il devient donc clair qui enregistre quoi.
Il exécute également install_mp_handler (), il devient donc inutile de l'exécuter avant de créer un pool.
Cela me permet de voir quel travailleur crée quels messages de journaux.
Voici le plan avec un exemple:
la source
À mes enfants qui rencontrent le même problème depuis des décennies et ont trouvé cette question sur ce site, je laisse cette réponse.
Simplicité vs complexité excessive. Utilisez simplement d'autres outils. Python est génial, mais il n'a pas été conçu pour faire certaines choses.
L'extrait suivant pour le démon logrotate fonctionne pour moi et ne complique pas les choses. Planifiez-le pour fonctionner toutes les heures et
Voici comment je l'installe (les liens symboliques ne fonctionnent pas pour logrotate):
la source