No momento, tenho um módulo central em uma estrutura que gera vários processos usando o multiprocessing
módulo Python 2.6 . Como ele usa multiprocessing
, há um log com reconhecimento de multiprocessamento no nível do módulo LOG = multiprocessing.get_logger()
. De acordo com os documentos , esse criador de logs possui bloqueios compartilhados por processos, para que você não detecte coisas sys.stderr
(ou qualquer outra manipulação de arquivo) ao ter vários processos gravando nele simultaneamente.
O problema que tenho agora é que os outros módulos na estrutura não são compatíveis com multiprocessamento. Na minha opinião, preciso fazer com que todas as dependências desse módulo central usem o log com reconhecimento de multiprocessamento. Isso é irritante dentro da estrutura, muito menos para todos os clientes da estrutura. Existem alternativas em que não estou pensando?
fonte
multiprocessing.get_logger()
? Parece que, com base nessas outras formas de registro, a funcionalidade de registro temmultiprocessing
pouco valor.get_logger()
é o logger usado pelomultiprocessing
próprio módulo. É útil se você deseja depurar ummultiprocessing
problema.Respostas:
A única maneira de lidar com isso de maneira não intrusiva é:
select
partir dos descritores de arquivo dos pipes, execute a classificação de mesclagem nas entradas de log disponíveis e limpe-as para o log centralizado. Repita.)fonte
atexit
:-). O problema é que ele não fornecerá uma leitura em tempo real. Isso pode fazer parte do preço do multiprocessamento, em oposição ao multithreading.multiprocessing.Queue
não será mais simples se há um monte de código para religação de usomultiprocessing.Queue
, e / ou se o desempenho é um problemaAcabei de escrever um manipulador de log que apenas envia tudo ao processo pai por meio de um pipe. Eu só o testei por dez minutos, mas parece funcionar muito bem.
( Nota: Este é codificado permanentemente
RotatingFileHandler
, que é meu próprio caso de uso.)Atualização: o @javier agora mantém essa abordagem como um pacote disponível no Pypi - consulte registro de multiprocessamento no Pypi, github em https://github.com/jruere/multiprocessing-logging
Atualização: Implementação!
Agora, isso usa uma fila para o tratamento correto da simultaneidade e também se recupera de erros corretamente. Agora estou usando isso em produção há vários meses, e a versão atual abaixo funciona sem problemas.
fonte
multiprocessing.Queue
utiliza um fio noput()
. Portanto, não chameput
(por exemplo, registre uma mensagem usandoMultiProcessingLog
manipulador) antes de criar todos os subprocessos. Caso contrário, o encadeamento ficará inoperante no processo filho. Uma solução é chamarQueue._after_fork()
no início de cada processo filho ou usá-lomultiprocessing.queues.SimpleQueue
, o que não envolve encadeamento, mas está bloqueando.multiprocessing-logging
.QueueHandler
é nativo no Python 3.2+ e faz exatamente isso. É facilmente replicado em versões anteriores.Os documentos Python têm dois exemplos completos: Registrando em um único arquivo a partir de vários processos
Para aqueles que usam Python <3.2, copie
QueueHandler
para seu próprio código em: https://gist.github.com/vsajip/591589 ou importe alternativamente logutils .Cada processo (incluindo o processo pai) coloca seu log no
Queue
e, em seguida, umlistener
thread ou processo (um exemplo é fornecido para cada) os seleciona e os grava em um arquivo - sem risco de corrupção ou distorção.fonte
Abaixo está outra solução com foco na simplicidade para qualquer pessoa (como eu) que chega aqui do Google. O registro deve ser fácil! Somente para 3,2 ou superior.
fonte
QueueHandler
e tambémQueueListener
podem ser usadas no Python 2.7, disponíveis nologutils
pacote.Ainda outra alternativa pode ser os vários manipuladores de log não baseados em arquivo no
logging
pacote :SocketHandler
DatagramHandler
SyslogHandler
(e outros)
Dessa forma, você pode facilmente ter um daemon de log em algum lugar no qual possa gravar com segurança e manipular os resultados corretamente. (Por exemplo, um servidor de soquete simples que apenas retira a mensagem e a emite em seu próprio manipulador de arquivo rotativo.)
O
SyslogHandler
cuidaria disso também para você. Obviamente, você poderia usar sua própria instânciasyslog
, não a do sistema.fonte
Uma variante das outras que mantém o segmento de log e fila separados.
fonte
fileConfig()
no MainProcess e um criador de logs mal configurado no PoolWorkers (apenassetLevel(logging.NOTSET)
). Como mencionei em outro comentário, estou usando Pool, então tive que obter minha Fila (proxy) do Manager em vez de multiprocessamento, para que pudesse ser decapada. Isso me permite passar a fila para um trabalhador dentro de um dicionário (a maioria dos quais é derivada do objeto argsparse usandovars()
). Sinto que, no final, essa é a melhor abordagem para o MS Windows que não possui o fork () e quebra a solução @zzzeak.fork
. Dessa forma, cada processo terá sua própria fila inútil e independente. A segunda abordagem na Q / A vinculada não funcionará nessas plataformas. É uma maneira de código não portátil.multiprocessing.Queue
com o processo principal e o uso constantemente desde então. Não pretendo entender por que funciona.Todas as soluções atuais estão muito acopladas à configuração de log usando um manipulador. Minha solução possui a seguinte arquitetura e recursos:
multiprocessing.Queue
logging.Logger
(e instâncias já definidas) são corrigidos para enviar todos os registros para a filaO código com exemplo de uso e saída pode ser encontrado na seguinte lista: https://gist.github.com/schlamar/7003737
fonte
daemon_thread.daemon
aTrue
. Eu precisava fazer isso para que meu programa Python saia corretamente quando ocorrer uma exceção no gerenciador de contexto.func
emlogged_call
, caso contrário, a exceção seria se ilegível com outra saída registrada. Aqui está minha versão modificada: gist.github.com/blah238/8ab79c4fe9cdb254f5c37abfc5dc85bfComo podemos representar o log de multiprocessos como muitos editores e um assinante (ouvinte), usar o ZeroMQ para implementar as mensagens do PUB-SUB é realmente uma opção.
Além disso, o módulo PyZMQ , as ligações Python para o ZMQ, implementa o PUBHandler , que é objeto para publicar mensagens de log em um soquete zmq.PUB.
Existe uma solução na Web , para registro centralizado de aplicativos distribuídos usando PyZMQ e PUBHandler, que pode ser facilmente adotado para trabalhar localmente com vários processos de publicação.
fonte
Eu também gosto da resposta de zzzeek, mas Andre está certo de que uma fila é necessária para evitar erros. Tive alguma sorte com o cachimbo, mas vi gargalhar o que é algo esperado. A implementação acabou sendo mais difícil do que eu pensava, principalmente devido à execução no Windows, onde existem algumas restrições adicionais sobre variáveis globais e outras coisas (consulte: Como o Multiprocessamento Python é implementado no Windows? )
Mas finalmente consegui funcionar. Esse exemplo provavelmente não é perfeito, portanto, comentários e sugestões são bem-vindos. Ele também não suporta a configuração do formatador ou qualquer outra coisa que não seja o registrador raiz. Basicamente, você precisa reiniciar o criador de logs em cada um dos processos do pool com a fila e configurar os outros atributos no criador de logs.
Novamente, todas as sugestões sobre como melhorar o código são bem-vindas. Eu certamente ainda não conheço todos os truques do Python :-)
fonte
if 'MainProcess' == multiprocessing.current_process().name:
pode ser usado no lugar da passagemchild
?basta publicar em algum lugar sua instância do logger. Dessa forma, os outros módulos e clientes podem usar sua API para obter o criador de logs sem a necessidade
import multiprocessing
.fonte
import logging; logging.basicConfig(level=logging.DEBUG); logging.debug('spam!')
de qualquer lugar e fazê-lo funcionar corretamente.Gostei da resposta do zzzeek. Gostaria apenas de substituir o Pipe por uma fila, pois, se vários threads / processos usarem o mesmo fim de pipe para gerar mensagens de log, eles ficarão ilegíveis.
fonte
Que tal delegar todo o log para outro processo que lê todas as entradas de log de uma fila?
Simplesmente compartilhe LOG_QUEUE através de qualquer um dos mecanismos de multiprocessos ou até mesmo herança e tudo funciona bem!
fonte
Eu tenho uma solução semelhante à da ironhacker, exceto pelo uso do logging.exception em alguns dos meus códigos e descobri que precisava formatar a exceção antes de transmiti-la pela Fila, pois os rastreamentos não são selecionáveis:
fonte
Abaixo está uma classe que pode ser usada no ambiente Windows, requer ActivePython. Você também pode herdar para outros manipuladores de log (StreamHandler etc.)
E aqui está um exemplo que demonstra o uso:
fonte
multiprocessing.Lock()
vez do Windows Mutex tornaria a solução portátil.Aqui está o meu truque / solução alternativa simples ... não o mais abrangente, mas facilmente modificável e mais simples de ler e entender, penso que qualquer outra resposta que encontrei antes de escrever isso:
fonte
Existe esse ótimo pacote
Pacote: https://pypi.python.org/pypi/multiprocessing-logging/
código: https://github.com/jruere/multiprocessing-logging
Instalar:
Adicione então:
fonte
Uma das alternativas é gravar o log do mutliprocessing em um arquivo conhecido e registrar um
atexit
manipulador para ingressar nesses processos; leia-o novamente no stderr; no entanto, você não receberá um fluxo em tempo real para as mensagens de saída no stderr dessa maneira.fonte
Se houver deadlocks em uma combinação de bloqueios, threads e garfos no
logging
módulo, isso é relatado no relatório de erros 6721 (consulte também a questão SO relacionada ).Há uma pequena solução de correção postada aqui .
No entanto, isso apenas corrigirá possíveis impasses
logging
. Isso não vai resolver que as coisas talvez estejam distorcidas. Veja as outras respostas apresentadas aqui.fonte
Idéia mais simples, como mencionado:
[WatchedFileHandler][1]
. As razões para esse manipulador são discutidas em detalhes aqui , mas, em suma, existem certas condições de corrida piores com os outros manipuladores de registro. Este tem a janela mais curta para a condição de corrida.fonte
Para quem precisar, escrevi um decorador para o pacote multiprocessing_logging que adiciona o nome do processo atual aos logs, para ficar claro quem registra o que.
Também executa install_mp_handler (), tornando-se inútil executá-lo antes de criar um pool.
Isso me permite ver qual trabalhador cria quais mensagens de log.
Aqui está o plano com um exemplo:
fonte
Aos meus filhos que encontram o mesmo problema em décadas e encontraram esta pergunta neste site, deixo esta resposta.
Simplicidade vs supercomplicar. Basta usar outras ferramentas. Python é incrível, mas não foi projetado para fazer algumas coisas.
O seguinte trecho para o daemon logrotate funciona para mim e não complica demais as coisas. Programe-o para executar a cada hora e
É assim que eu o instalo (os links simbólicos não funcionam para o logrotate):
fonte