Armazena a saída de um comando em um buffer de anel

14

Eu tenho um comando de longa execução que gera muita saída no stdout. Eu gostaria de ser capaz de manter, por exemplo, apenas os últimos três dias ou o último gibibyte (evitando linhas de corte no meio) e, se possível, em blocos de arquivos com tamanho não superior a 20 MiB. Cada pedaço de arquivo está sendo nomeado com um sufixo numérico ou timestamp.

Algo como:

my-cmd | magic-command --output-file-template=my-cmd-%t \
                       --keep-bytes=1G --keep-time=3d \
                       --max-chunk-size=20M
                       --compress=xz

Escreveria:

my-cmd-2014-09-05Z10:04:23

Quando chegar a 20M, ele será compactado e abrirá um novo, e assim por diante, e depois de algum tempo, ele começará a excluir os arquivos mais antigos.

Existe tal comando?

Estou ciente de logrotate e sua capacidade de gerenciar arquivos gravados por outros aplicativos, mas estou procurando algo mais simples que não envolva a necessidade de configurar uma tarefa cron, especificar regras, suspender o processo, etc.

    
por Stéphane Chazelas 04.08.2014 / 12:25

4 respostas

5

Você pode obter um pouco do que deseja por meio do pipelog , que "permite girar ou limpar o registro de um processo em execução canalizando-o através de um intermediário que responde a sinais externos ", por exemplo:

spewstuff | pipelog spew.log -p /tmp/spewpipe.pid -x "gzip spew.log.1"

Você pode então obter o pid em /tmp/spewpipe.pid e:

kill -s USR1 $(</tmp/spewpipe.pid)

Mas você teria que configurar com o cron ou algo assim. Há uma pegadinha nisso, no entanto. Observe que gzip spew.log.1 - isso ocorre porque o comando -x é executado depois que o log é rotacionado. Então você tem o problema adicional de sobrescrever spew.log.1.gz a cada vez, a menos que você escreva um script curto para fazer o gzip e mova o arquivo depois, e use isso como o comando -x .

Divulgação completa: eu escrevi isso, então é claro que funciona perfeitamente . ;) Manterei uma opção de compactação em mente, ou algo que a facilite melhor, para a versão 0.2 (a finalidade pretendida de -x é um pouco diferente, mas funcionará como acima). Também o rollover automatizado é uma boa idéia ... a primeira versão é intencionalmente mínima, pois resisti à tentação de adicionar recursos que não eram necessários (afinal, não é tão difícil configurar uma tarefa cron para isso).

Note que ele é destinado à saída texto ; se houver bytes nulos em potencial, você deve usar -z - que substitui o zero por outra coisa. Esta foi uma troca para simplificar a implementação.

    
por 04.08.2014 / 13:58
3

O multilog de Dan Bernstein aparentemente pode fazer isso - ou talvez a maior parte dele, enquanto fornece uma saída por meio de descritores de arquivos para ! processador para compensar a diferença que você desejar - embora as especificações de tamanho 20M / 1G possam levar alguns recursos, uma vez que 16M é o limite externo por log. O que segue é, na maioria, uma seleção copiar + colar do link acima, embora o link também detalhe outras opções, como o registro de data e hora por linha, mantendo [um] outro arquivo [s] contendo apenas a linha mais recente correspondente padrão e mais.

Interface

 multilog script

... script consiste em qualquer número de argumentos. Cada argumento especifica uma ação. As ações são executadas para cada linha de entrada.

Selecionando linhas

Cada linha é inicialmente selecionada. A ação ...

-pattern

... desmarca a linha se o padrão corresponder à linha. A ação ...

+pattern

seleciona a linha se padrão corresponder à linha.

... padrão é uma cadeia de estrelas e não estrelas. Ele corresponde a qualquer concatenação de strings correspondidas por todas as estrelas e não estrelas na mesma ordem. Uma não-estrela corresponde a si mesma. Uma estrela antes do final do padrão corresponde a qualquer string que não inclua o próximo caractere no padrão. Uma estrela no final do padrão corresponde a qualquer string.

Registros girados automaticamente

Se dir começar com um ponto ou barra, a ação ...

 dir

... anexa cada linha selecionada a um log chamado dir . Se dir não existir, multilog o cria.

O formato do log é o seguinte:

  1. dir é um diretório contendo algum número de arquivos de log antigos, um arquivo de log chamado atual e outros arquivos para multilog para manter o controle de seus arquivos. ações.

  2. Cada arquivo de log antigo tem um nome que começa com @ , continuando com um registro de data e hora preciso mostrando quando o arquivo foi concluído e terminando com um dos seguintes códigos:

    • .s : Este arquivo é completamente processado e gravado com segurança em disco.
    • .u : Este arquivo estava sendo criado no momento de uma interrupção. Pode ter sido truncado. Não foi processado.

A ação ...

 ssize

... define o tamanho máximo do arquivo para ações subseqüentes de dir . multilog decidirá que atual é grande o suficiente se atual tiver tamanho bytes. ( multilog também decidirá que a corrente é grande o suficiente se vir uma nova linha em 2000 bytes do tamanho máximo do arquivo; ela tenta terminar os arquivos de log nos limites da linha.) tamanho deve estar entre 4096 e 16777215. O tamanho de arquivo máximo padrão é 99999.

Nas versões 0.75 e superiores: Se multilog receber um sinal ALRM , ele decide imediatamente que atual é grande o suficiente, se atual não é vazio.

(Nota: Eu suspeito que o zsh schedule builtin poderia ser facilmente persuadido a enviar um ALRM em intervalos especificados, se necessário.)

A ação ...

 nnum

... define o número de arquivos de log para as ações dir subsequentes. Depois de renomear atual , se multilog visualizar num ou mais arquivos de log antigos, ele removerá o arquivo de log antigo com o menor registro de data e hora. num deve ser pelo menos 2. O número padrão de arquivos de log é 10.

A ação ...

 !processor

... define um processador para ações subseqüentes dir . multilog alimentará atual através do processador e salvará a saída como um arquivo de log antigo em vez de atual . multilog também salvará qualquer saída que o processador grava no descritor 5 e tornará a saída legível no descritor 4 quando executar o processador no próximo arquivo de log. Para confiabilidade, o processador deve ser diferente de zero se tiver algum problema ao criar sua saída; multilog executará novamente. Note que a execução do processador pode bloquear qualquer entrada de alimentação do programa para multilog .

    
por 25.09.2014 / 21:22
2

O melhor que eu pude encontrar até agora como uma aproximação que não envolva escrever grandes pedaços de código é esse zsh code:

autoload zmv
mycmd |
  while head -c20M > mycmd.log && [ -s mycmd.log ]; do
    zmv -f '(mycmd.log)(|.(<->))(|.gz)(#qnOn)' '$1.$(($3+1))$4'
    {rm -f mycmd.log.1 mycmd.log.50.gz; (gzip&) > mycmd.log.1.gz} < mycmd.log.1
  done

Aqui dividindo e girando em, no máximo, 51 arquivos grandes de 20MiB.

    
por 04.08.2014 / 17:40
2

Aqui está um script python hackeado para fazer algo parecido com o que você está solicitando:

#!/bin/sh
''':'
exec python "$0" "$@"
'''

KEEP = 10
MAX_SIZE = 1024 # bytes
LOG_BASE_NAME = 'log'

from sys import stdin
from subprocess import call

log_num = 0
log_size = 0
log_name = LOG_BASE_NAME + '.' + str(log_num)
log_fh = open(log_name, 'w', 1)

while True:
        line = stdin.readline()
        if len(line) == 0:
                log_fh.close()
                call(['gzip', '-f', log_name])
                break
        log_fh.write(line)
        log_size += len(line)
        if log_size >= MAX_SIZE:
                log_fh.close()
                call(['gzip', '-f', log_name])
                if log_num < KEEP:
                        log_num += 1
                else:
                        log_num = 0
                log_size = 0
                log_name = LOG_BASE_NAME + '.' + str(log_num)
                log_fh = open(log_name, 'w', 1)
    
por 05.08.2014 / 20:54