Eu tenho um programa cuja saída eu redireciono para um arquivo de log:
./my_app > log
Gostaria de limpar (ou seja, esvaziar) o log de tempos em tempos (sob demanda) e tentei várias coisas como
cat "" > log
No entanto, sempre parece que o canal original é interrompido e o programa não redireciona sua saída para o arquivo de log.
Existe alguma maneira de fazer isso?
Atualizar
Observe que não posso modificar o aplicativo que produz a saída. Ele apenas cospe no stdout e eu quero salvá-lo em um log para que eu possa inspecioná-lo quando precisar e limpá-lo quando quiser. No entanto, não preciso reiniciar o aplicativo.
bash
io-redirection
bangnab
fonte
fonte
syslogd
oulogrotate
./my_app >> log
(para forçar a adição) ecp /dev/null log
truncá-lo?cat "" > log
não é umcat
comando válido, pois não há arquivo chamado""
.Respostas:
Outra forma desse problema ocorre com aplicativos de execução longa cujos logs são rotacionados periodicamente. Mesmo se você mover o log original (por exemplo,
mv log.txt log.1
) e substituí-lo imediatamente por um arquivo com o mesmo nome antes que ocorra qualquer log real, se o processo estiver mantendo o arquivo aberto, ele acabará gravandolog.1
(porque isso ainda pode ser o inode aberto) ou para nada.Uma maneira comum de lidar com isso (o próprio criador de logs do sistema funciona dessa maneira) é implementar um manipulador de sinais no processo que fechará e reabrirá seus logs. Então, sempre que você desejar mover ou limpar (excluindo) o log, envie esse sinal para o processo imediatamente depois.
Aqui está uma demonstração simples do bash - perdoe minhas habilidades cruas de shell (mas se você quiser editar isso para obter boas práticas etc.), entenda a funcionalidade primeiro e teste sua revisão antes de editar:
Comece isso entrando em segundo plano:
Observe que ele reporta seu PID ao terminal e começa a fazer logon no
log.txt
. Agora você tem 2 minutos para brincar. Aguarde alguns segundos e tente:Simplesmente
kill -2 12356
pode funcionar para você aqui também. O sinal 2 é SIGINT (também é o que Ctrl-C faz, para que você possa tentar isso em primeiro plano e mover ou remover o arquivo de log de outro terminal), quetrap
deve ser interceptado. Checar;Agora vamos ver se ele ainda está gravando em um
log.txt
arquivo, mesmo que o tenhamos movido:Observe que ele continuou indo exatamente de onde parou. Se você não deseja manter o registro, basta limpar o registro excluindo-o
Verifica:
Ainda indo.
Você não pode fazer isso em um script de shell para um subprocesso executado, infelizmente, porque, se estiver em primeiro plano, os manipuladores de sinal do bash
trap
serão suspensos e, se você o colocar em segundo plano, não poderá redesigná-lo. resultado. Ou seja, isso é algo que você precisa implementar em seu aplicativo.Contudo...
Se você não pode modificar o aplicativo (por exemplo, porque você não o escreveu), eu tenho um utilitário CLI que você pode usar como intermediário. Você também pode implementar uma versão simples disso em um script que serve como canal para o log:
Vamos chamar assim
pipetrap.sh
. Agora precisamos de um programa separado para testar, imitando o aplicativo que você deseja registrar:Isso será
test.sh
:Esses são dois processos separados com PIDs separados. Para limpar
test.sh
a saída, que está sendo canalizada através depipetrap.sh
:Verifica:
15858,,
test.sh
ainda está em execução e sua saída está sendo registrada. Nesse caso, nenhuma modificação no aplicativo é necessária.fonte
TL; DR
Abra seu arquivo de log no modo de acréscimo :
Em seguida, você pode truncá-lo com segurança com:
Detalhes
Com um shell semelhante ao Bourne, existem três maneiras principais de abrir um arquivo para gravação. No modo somente gravação (
>
), leia + escreva (<>
) ou acrescente (e somente gravação>>
).Nos dois primeiros, o kernel se lembra da posição atual em que você (por você, quero dizer, a descrição do arquivo aberto , compartilhada por todos os descritores de arquivo que o duplicaram ou herdaram ao bifurcar aquele em que você abriu o arquivo) está no diretório Arquivo.
Quando você faz:
log
está aberto no modo somente gravação pelo shell para o stdout decmd
.cmd
(seu processo inicial gerado pelo shell e todos os filhos possíveis) ao escrever em seu stdout, escreva na posição atual do cursor mantida pela descrição do arquivo aberto que eles compartilham nesse arquivo.Por exemplo, se gravar
cmd
inicialmentezzz
, a posição será no deslocamento de bytes 4 no arquivo e, na próxima vez em quecmd
seus filhos gravarem no arquivo, é onde os dados serão gravados, independentemente de o arquivo ter aumentado ou diminuído no intervalo .Se o arquivo encolheu, por exemplo, se ele foi truncado com um
e
cmd
gravaçõesxx
, elasxx
serão gravadas em offset4
e os 3 primeiros caracteres serão substituídos por caracteres NUL.Isso significa que você não pode truncar um arquivo que foi aberto no modo somente gravação (e é o mesmo para leitura + gravação ) como se o fizesse, processos que tinham descritores de arquivo abertos no arquivo, deixarão caracteres NUL no início do arquivo (aqueles, exceto no OS / X, normalmente não ocupam espaço no disco, eles se tornam arquivos esparsos).
Em vez disso (e você notará que a maioria dos aplicativos faz isso quando escreve nos arquivos de log), você deve abrir o arquivo no modo de acréscimo :
ou
se você deseja iniciar em um arquivo vazio.
No modo de acréscimo, todas as gravações são feitas no final do arquivo, independentemente de onde a última gravação foi:
Isso também é mais seguro, como se dois processos tivessem aberto (dessa maneira) o arquivo por engano (como por exemplo, se você iniciou duas instâncias do mesmo daemon), a saída deles não será substituída.
Nas versões recentes do Linux, você pode verificar a posição atual e se um descritor de arquivo foi aberto no modo de acréscimo , observando
/proc/<pid>/fdinfo/<fd>
:Ou com:
Esses sinalizadores correspondem aos sinalizadores O ..._ passados para a
open
chamada do sistema.(
O_APPEND
é 0x400 ou octal 02000)Portanto, o shell
>>
abre o arquivo comO_WRONLY|O_APPEND
(e 0100000 aqui é O_LARGEFILE, o que não é relevante para esta questão) enquanto>
éO_WRONLY
apenas (e<>
éO_RDWR
apenas).Se você fizer um:
para procurar arquivos abertos
O_APPEND
, você encontrará a maioria dos arquivos de log atualmente abertos para gravação em seu sistema.fonte
:
(dois pontos) em: >
?:
. Sem um comando, o comportamento varia entre conchas.Se estou entendendo corretamente,
tee
parece uma abordagem razoável:fonte
Como solução rápida, você pode usar um log com rotação (rotação diária, por exemplo):
e redirecione o log para ele
./my_app >> log$date.log
fonte
Este é um problema que há muito tempo foi resolvido com o syslog (em todas as suas variantes), mas existem duas ferramentas que resolveriam seu problema específico com o mínimo de esforço.
A primeira solução, mais portátil, mas menos versátil, é o logger (obrigatório para qualquer caixa de ferramentas de administradores). É um utilitário simples que copia a entrada padrão para o syslog. (passando a bola e tornando a rotação de arquivos o problema do logrotate e do syslog)
A segunda solução, mais elegante, mas menos portátil, é o syslog-ng, que além de aceitar mensagens de log dos soquetes de syslog padrão, pode executar programas cuja saída é filtrada pelo logger. (Ainda não usei esse recurso, mas parece perfeito para o que você deseja fazer.)
fonte