Edit: Como parece que não há solução ou estou fazendo algo tão fora do padrão que ninguém sabe - revisarei minha pergunta para perguntar também: Qual é a melhor maneira de realizar o log quando um aplicativo python está fazendo um muitas chamadas de sistema?
Meu aplicativo tem dois modos. No modo interativo, quero que toda a saída vá para a tela e também para um arquivo de log, incluindo a saída de qualquer chamada do sistema. No modo daemon, toda a saída vai para o log. O modo Daemon funciona muito bem usando os.dup2()
. Não consigo encontrar uma maneira de "tee" toda a saída para um log no modo interativo, sem modificar cada chamada do sistema.
Em outras palavras, desejo a funcionalidade da linha de comando 'tee' para qualquer saída gerada por um aplicativo python, incluindo saída de chamada do sistema .
Esclarecer:
Para redirecionar todas as saídas, faço algo assim, e funciona muito bem:
# open our log file
so = se = open("%s.log" % self.name, 'w', 0)
# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
O bom disso é que ele não requer chamadas de impressão especiais do restante do código. O código também executa alguns comandos do shell, por isso é bom não ter que lidar com cada saída individualmente também.
Simplesmente, quero fazer o mesmo, exceto duplicar em vez de redirecionar.
À primeira vista, pensei que simplesmente reverter os dup2
funcionaria. Por que não? Aqui está o meu teste:
import os, sys
### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###
print("foo bar")
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)
O arquivo "a.log" deve ser idêntico ao que foi exibido na tela.
Respostas:
Como você se sente confortável gerando processos externos a partir do seu código, você pode usar a
tee
si próprio. Não conheço nenhuma chamada de sistema Unix que faça exatamente o quetee
faz.Você também pode emular
tee
usando o pacote de multiprocessamento (ou usar o processamento se estiver usando o Python 2.5 ou anterior).Atualizar
Aqui está uma versão compatível com Python 3.3 +:
fonte
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
não funciona mais desde python 3.3 (ver PEP 3116)tee.stdin.close()
no final do meu programa. Também recebo "ResourceWarning: o subprocesso 1842 ainda está em execução" e a adiçãosys.stdout.close(); sys.stderr.close()
no final do programa o corrige.Eu tive esse mesmo problema antes e achei este snippet muito útil:
from: http://mail.python.org/pipermail/python-list/2007-May/438106.html
fonte
__del__
não é chamado até o final da execução. Veja stackoverflow.com/questions/6104535/…A
print
instrução chamará owrite()
método de qualquer objeto que você atribuir ao sys.stdout.Gostaria de criar uma turma pequena para escrever para dois lugares ao mesmo tempo ...
Agora, a
print
instrução ecoará na tela e será anexada ao seu arquivo de log:Isto é obviamente rápido e sujo. Algumas notas:
<stdout>
se não estiver registrando durante o programa.Tudo isso é direto o suficiente para que eu me sinta confortável em deixá-los como exercícios para o leitor. A principal dica aqui é que
print
apenas chama um "objeto semelhante a arquivo" ao qual está atribuídosys.stdout
.fonte
The print statement will call the write() method of any object you assign to sys.stdout
. E quanto a outras funções que enviam dados para o stdout que não está usandoprint
. Por exemplo, se eu criar um processo usandosubprocess.call
sua saída, vá para o console, mas não para olog.dat
arquivo ... existe uma maneira de corrigir isso?O que você realmente deseja é um
logging
módulo da biblioteca padrão. Crie um logger e anexe dois manipuladores, um gravando em um arquivo e o outro em stdout ou stderr.Consulte Registrando em vários destinos para obter detalhes
fonte
logging
módulo de saída não redirecionamento de chamadas do sistema, tais comoos.write(1, b'stdout')
Aqui está outra solução, que é mais geral que as outras - ele suporta a divisão de saída (gravada em
sys.stdout
) para qualquer número de objetos semelhantes a arquivos. Não há exigência de que__stdout__
ele esteja incluído.NOTA: Essa é uma prova de conceito. A implementação aqui não está completa, pois envolve apenas métodos de objetos semelhantes a arquivos (por exemplo
write
), excluindo membros / propriedades / setattr, etc. No entanto, provavelmente é boa o suficiente para a maioria das pessoas, como está atualmente.O que eu gosto sobre isso, a não ser sua generalidade, é que ele é limpo, no sentido de não fazer quaisquer chamadas diretas para
write
,flush
,os.dup2
, etc.fonte
_wrap
aqui? Você não conseguiu copiar o código__getattr__
e o mesmo funciona?multifile([])
cria um arquivo que gera umUnboundLocalError
sempre que você chama um de seus métodos. (res
É devolvido sem ser atribuído)Como descrito em outro lugar, talvez a melhor solução seja usar o módulo de registro diretamente:
No entanto, existem algumas (raras) ocasiões em que você realmente deseja redirecionar o stdout. Eu tive essa situação quando estendia o comando runserver do django, que usa print: não queria invadir a fonte do django, mas precisava das instruções de impressão para ir para um arquivo.
Esta é uma maneira de redirecionar stdout e stderr para fora do shell usando o módulo de log:
Você só deve usar esta implementação do LogFile se realmente não puder usar o módulo de log diretamente.
fonte
Eu escrevi uma
tee()
implementação em Python que deve funcionar na maioria dos casos e funciona no Windows também.https://github.com/pycontribs/tendo
Além disso, você pode usá-lo em combinação com o
logging
módulo do Python, se desejar.fonte
(Ah, basta reler sua pergunta e ver se isso não se aplica.)
Aqui está um exemplo de programa que utiliza o módulo de log python . Este módulo de registro está em todas as versões desde 2.3. Nesta amostra, o log é configurável pelas opções da linha de comandos.
No modo completo, ele registrará apenas em um arquivo; no modo normal, registrará em um arquivo e no console.
fonte
Para concluir a resposta de John T: https://stackoverflow.com/a/616686/395687
Eu adicionei
__enter__
e__exit__
métodos para usá-lo como um gerenciador de contexto com awith
palavra - chave, que fornece esse códigoPode então ser usado como
fonte
__del__
funcionalidade para__exit__
__del__
é uma má ideia. Ele deve ser movido para uma função 'close' que é chamada__exit__
.Sei que esta pergunta foi respondida repetidamente, mas, para isso, peguei a resposta principal da resposta de John T e a modifiquei para que ela contenha o flush sugerido e segui sua versão revisada vinculada. Também adicionei o enter e exit, como mencionado na resposta do cladmi para uso com a instrução with. Além disso, a documentação menciona a liberação de arquivos usando,
os.fsync()
então eu adicionei isso também. Não sei se você realmente precisa disso, mas está lá.Você pode usá-lo
ou
fonte
mode="ab"
e nawrite
funçãoself.file.write(message.encode("utf-8"))
outra solução usando o módulo de log:
fonte
Nenhuma das respostas acima parece realmente responder ao problema proposto. Eu sei que esse é um tópico antigo, mas acho que esse problema é muito mais simples do que todo mundo está criando:
Agora isso repetirá tudo para o manipulador sys.stderr normal e seu arquivo. Crie outra classe
tee_out
parasys.stdout
.fonte
tee=tee_err();tee.write('');tee.write('');...
abre + fecha um arquivo para cadawrite
. Consulte stackoverflow.com/q/4867468 e stackoverflow.com/q/164053 para obter argumentos contra essa prática.De acordo com uma solicitação de @ user5359531 nos comentários na resposta de @John T , aqui está uma cópia da postagem referenciada na versão revisada da discussão vinculada nessa resposta:
fonte
Estou escrevendo um script para executar scripts de linha cmd. (Porque em alguns casos, simplesmente não há substituto viável para um comando Linux - como o caso do rsync.)
O que eu realmente queria era usar o mecanismo de log python padrão em todos os casos em que fosse possível fazê-lo, mas ainda assim capturar qualquer erro quando algo der errado que não era previsto.
Este código parece fazer o truque. Pode não ser particularmente elegante ou eficiente (embora não use string + = string, pelo menos não possui esse potencial gargalo em particular). Estou publicando, caso isso dê a alguém alguma idéia útil.
Obviamente, se você não está tão sujeito a um capricho quanto eu, substitua LOG_IDENTIFIER por outra sequência que você não gostaria de ver alguém escrever em um log.
fonte
Se você deseja registrar toda a saída em um arquivo E enviá-la para um arquivo de texto, faça o seguinte. É um pouco hacky, mas funciona:
EDIT: Observe que isso não registra erros, a menos que você redirecione sys.stderr para sys.stdout
EDIT2: Uma segunda questão é que você precisa passar 1 argumento diferente da função interna.
EDIT3: Veja o código antes para gravar stdin e stdout no console e arquivo, com o stderr indo apenas para o arquivo
fonte
Eu escrevi um substituto completo para
sys.stderr
e apenas dupliquei o código renomeandostderr
parastdout
torná-lo disponível também para substituirsys.stdout
.Para fazer isso eu criar o mesmo tipo de objeto como a corrente
stderr
estdout
, e encaminhar todos os métodos para o sistema originalstderr
estdout
:Para usar isso, basta ligar
StdErrReplament::lock(logger)
eStdOutReplament::lock(logger)
passar o logger que deseja usar para enviar o texto de saída. Por exemplo:Executando este código, você verá na tela:
E no conteúdo do arquivo:
Se você também quiser ver o conteúdo das
log.debug
chamadas na tela, precisará adicionar um manipulador de fluxo ao seu criador de logs. Nesse caso, seria assim:Qual seria o resultado dessa saída ao executar:
Enquanto isso ainda salvaria isso no arquivo
my_log_file.txt
:Ao desabilitar isso
StdErrReplament:unlock()
, ele restaurará apenas o comportamento padrão dostderr
fluxo, pois o criador de logs anexado nunca poderá ser desanexado porque outra pessoa pode ter uma referência à sua versão mais antiga. É por isso que é um singleton global que nunca morre. Portanto, no caso de recarregar este móduloimp
ou algo mais, ele nunca recuperará a correntesys.stderr
como já foi injetada e a salvará internamente.fonte