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


16

Eu tenho um comando de longa duração que gera muita saída no stdout. Eu gostaria de poder reter, 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 não maiores que 20 MiB. Cada parte do arquivo está sendo nomeada com um sufixo numérico ou um carimbo de data / hora.

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-05T10:04:23Z

Quando chega aos 20 milhões, o compacta e abre um novo, e assim por diante e depois de um tempo começa a excluir os arquivos mais antigos.

Existe um comando desse tipo?

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


O que é um "gibibyte"?
Peter Mortensen

@PeterMortensen Wikipedia: Gibibyte
jw013

Respostas:


6

Você pode obter o que deseja via pipelog , que "permite girar ou limpar o log 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 obter o pid /tmp/spewpipe.pide:

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

Mas que você teria que configurar com cron ou algo assim. Há um problema nisso, no entanto. Observe I gzip spew.log.1- isso ocorre porque o -xcomando é executado após o log ser rotacionado. Portanto, você terá o problema adicional de sobrescrever a spew.log.1.gzcada vez, a menos que escreva um script curto para executar o gzip e depois mova o arquivo, e use-o como -xcomando.

Divulgação completa: eu escrevi isso, então é claro que funciona perfeitamente . ;) Manterei em mente uma opção de compactação, ou algo que a facilite melhor, para a versão 0.2 (o objetivo pretendido -xé um pouco diferente, mas funcionará como acima). A rolagem automatizada também é uma boa ideia ... 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 um trabalho cron para isso).

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


Obrigado. Estou ansioso para pipelog-0.3;-). Eu também me deparei com metacpan.org/release/File-Write-Rotate . Observe que os trabalhos cron não ajudarão muito na rotação com base no tamanho do arquivo.
Stéphane Chazelas

Rodar com base no tamanho!?! Ele faz manter a saída liberado, para que você possa executar stat o arquivo em intervalos ...
Goldilocks

Você não podia manter o tamanho abaixo de 20 milhões (como nos requisitos da minha pergunta) dessa maneira.
Stéphane Chazelas

A outra coisa é que é basicamente apenas texto (adicionei um parágrafo final sobre isso).
precisa saber é o seguinte

4

O multilog de Dan Bernstein pode aparentemente fazer isso - ou talvez a maioria, enquanto fornece uma saída via descritores de arquivo para o processador! Para compensar a diferença que você gosta - embora as especificações de tamanho 20M / 1G possam levar algum tempo, pois parece que 16M é o seu limite externo por log. O que se segue é, na maioria das vezes, uma seleção de copiar e colar no link acima, embora o link também detalhe outras opções, como registro de data e hora por linha, mantendo [outros] outros arquivos contendo apenas o padrão de correspondência de linhas mais recente 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 em ordem para cada linha de entrada.

Selecionando linhas

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

-pattern

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

+pattern

seleciona a linha se o padrão corresponder à linha.

... padrão é uma cadeia de estrelas e não estrelas. Corresponde a qualquer concatenação de cadeias 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 sequência que não inclua o próximo caractere no padrão. Uma estrela no final do padrão corresponde a qualquer sequência.

Logs girados automaticamente

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

 dir

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

O formato do log é o seguinte:

  1. dir é um diretório que contém alguns arquivos de log antigos, um arquivo de log chamado current e outros arquivos para multilogacompanhar suas ações.

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

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

A acção...

 ssize

... define o tamanho máximo do arquivo para ações dir subsequentes . multilogdecidirá que a corrente é grande o suficiente se a corrente tiver bytes de tamanho . ( multilogtambém decidirá que a corrente é grande o suficiente se vir uma nova linha dentro de 2000 bytes do tamanho máximo do arquivo; tenta finalizar os arquivos de log nos limites da linha.) O tamanho deve estar entre 4096 e 16777215. O tamanho máximo padrão do arquivo é 99999.

Nas versões 0.75 e acima: Se multilogreceber um sinal ALRM , ele imediatamente decide que a corrente é grande o suficiente, se a corrente não for vazia.

(Nota: Eu suspeito que o zsh schedulecomponente interno possa ser facilmente persuadido a enviar um ALRMem intervalos especificados, se necessário.)

A acção...

 nnum

... define o número de arquivos de log para ações de diretório subseqüentes . Depois de renomear atual , se multilognum ou arquivos de log mais velhos, ele remove o arquivo de log antigo com o menor timestamp. num deve ser pelo menos 2. O número padrão de arquivos de log é 10.

A acção...

 !processor

... define um processador para ações dir subsequentes . multilogalimentará a corrente através do processador e salvará a saída como um arquivo de log antigo em vez de atual . multilogtambém salvará qualquer saída gravada pelo processador no descritor 5 e tornará essa saída legível no descritor 4 quando executar o processador no próximo arquivo de log. Para garantir a confiabilidade, o processador deve sair diferente de zero se houver algum problema ao criar sua saída; multilogirá executá-lo novamente. Observe que o processador em execução pode bloquear qualquer entrada de alimentação do programa multilog.


2

O melhor que pude encontrar até agora como uma aproximação que não envolva escrever grandes partes de código é este zshcódigo:

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, divida e gire em no máximo 51 arquivos grandes de 20MiB.


talvez ... montagens em loop? btrfstambém pode ser montado com compress-force=zlib.
mikeserv

2

Aqui está um script python hackeado para fazer algo como 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)

1
Existe uma razão para tê-lo como um script de shell que execé python como a primeira coisa em vez de usar o hashbang pythonou env python?
Peterph
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.