Memorizza l'output di un comando in un buffer ad anello


16

Ho un comando di lunga durata che genera molto output su stdout. Vorrei poter conservare, ad esempio, solo gli ultimi tre giorni o l'ultimo gibibyte (evitando le linee di taglio nel mezzo) e, se possibile, in blocchi di file non più grandi di 20 MiB. Ogni blocco di file viene nominato con un suffisso numerico o un timestamp.

Qualcosa di simile a:

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

Scriverei:

my-cmd-2014-09-05T10:04:23Z

Quando raggiunge i 20 milioni, lo comprime e ne apre uno nuovo, e così via, e dopo un po 'inizia a cancellare i file più vecchi.

Esiste un comando del genere?

Sono a conoscenza logrotatee la sua capacità di gestire i file scritti da altre applicazioni, ma sto cercando qualcosa di più semplice che non implichi la necessità di impostare un lavoro cron, specificare regole, sospendere il processo, ecc.


Che cos'è un "gibibyte"?
Peter Mortensen,

@PeterMortensen Wikipedia: Gibibyte
jw013,

Risposte:


6

È possibile ottenere ciò che si desidera tramite il pipelog , che "consente di ruotare o cancellare il registro di un processo in esecuzione eseguendo il piping attraverso un intermedio che risponde a segnali esterni", ad esempio:

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

È quindi possibile ottenere il pid da /tmp/spewpipe.pide:

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

Ma che dovresti impostare con cron o qualcosa del genere. C'è un problema in questo, tuttavia. Nota I gzip spew.log.1: questo perché il -xcomando viene eseguito dopo la rotazione del registro. Quindi hai l'ulteriore problema di sovrascrivere spew.log.1.gzogni volta a meno che tu non scriva un breve script per fare il gzip e spostare il file in seguito, e usarlo come -xcomando.

Divulgazione completa: ho scritto questo, quindi ovviamente funziona perfettamente . ;) Terrò presente un'opzione di compressione, o qualcosa che la facilita meglio, per la versione 0.2 (lo scopo previsto -xè leggermente diverso, ma funzionerà come sopra). Anche il rollover automatizzato è una buona idea ... la prima versione è intenzionalmente minima poiché ho resistito alla tentazione di aggiungere funzionalità che non erano necessarie (non è così difficile impostare un lavoro cron per questo, dopo tutto).

Si noti che è destinato all'output del testo ; se ci sono potenziali byte null, dovresti usare -z- che sostituisce lo zero con qualcos'altro. Questo è stato un compromesso per semplificare l'implementazione.


Grazie. Non vedo l'ora di pipelog-0.3;-). Mi sono anche imbattuto in metacpan.org/release/File-Write-Rotate . Si noti che i lavori cron non saranno di grande aiuto per la rotazione in base alla dimensione del file.
Stéphane Chazelas,

Rotante in base alla dimensione!?! Mantiene l'output svuotato, in modo da poter impostare il file a intervalli ...
goldilocks

Non è possibile mantenere le dimensioni inferiori a 20 M (come nei miei requisiti di domanda) in modo affidabile.
Stéphane Chazelas,

L'altra cosa è che è praticamente solo testo (ho aggiunto un paragrafo finale a riguardo).
Riccioli d'oro

4

Di Dan Bernstein multilog apparentemente può fare questo - o forse la maggior parte di esso, fornendo al contempo una presa tramite descrittori di file per il processore! Per compensare la differenza come ti piace - anche se i 20m / specifiche dimensioni 1G possono richiedere un certo finagling come sembra 16M è la sua limite esterno per registro. Quello che segue è, nella maggior parte dei casi, una selezione copia + incolla dal link sopra, sebbene il link descriva in dettaglio anche altre opzioni come il timestamp per riga, mantenendo [un] altro file [s] contenente solo il modello di corrispondenza della riga più recente e altro .

Interfaccia

 multilog script

... lo script è composto da un numero qualsiasi di argomenti. Ogni argomento specifica un'azione. Le azioni vengono eseguite in ordine per ogni riga di input.

Selezione delle linee

Ogni riga è inizialmente selezionata. L'azione...

-pattern

... deseleziona la linea se il motivo corrisponde alla linea. L'azione...

+pattern

seleziona la linea se il motivo corrisponde alla linea.

... il modello è una serie di stelle e non stelle. Corrisponde a qualsiasi concatenazione di stringhe corrispondenti a tutte le stelle e alle non stelle nello stesso ordine. Una non stella corrisponde a se stessa. Una stella prima della fine del modello corrisponde a qualsiasi stringa che non include il carattere successivo nel modello. Una stella alla fine del motivo corrisponde a qualsiasi stringa.

Registri ruotati automaticamente

Se dir inizia con un punto o una barra quindi l'azione ...

 dir

... aggiunge ogni riga selezionata a un registro chiamato dir . Se dir non esiste, lo multilogcrea.

Il formato del registro è il seguente:

  1. dir è una directory che contiene un numero di vecchi file di registro, un file di registro denominato corrente e altri file per multilogtenere traccia delle sue azioni.

  2. Ogni vecchio file di registro ha un nome che inizia con @ , continua con un timestamp preciso che mostra quando il file è stato terminato e termina con uno dei seguenti codici:

    • .s : questo file è completamente elaborato e scritto in modo sicuro su disco.
    • .u : questo file è stato creato al momento di un'interruzione. Potrebbe essere stato troncato. Non è stato elaborato

L'azione...

 ssize

... imposta la dimensione massima del file per le successive azioni dir . multilogdeciderà che la corrente è abbastanza grande se corrente ha byte di dimensione . ( multilogdeciderà anche che la corrente è abbastanza grande se vede una nuova riga entro 2000 byte della dimensione massima del file; prova a finire i file di registro ai limiti della linea.) La dimensione deve essere compresa tra 4096 e 16777215. La dimensione massima del file predefinita è 99999.

Nelle versioni 0.75 e successive: se multilogriceve un segnale ALRM , decide immediatamente che la corrente è abbastanza grande, se la corrente è non vuota.

(Nota: sospetto che il zsh schedulebuiltin potrebbe essere facilmente persuaso a inviare un ALRMintervallo specificato se necessario.)

L'azione...

 nnum

... imposta il numero di file di registro per le successive azioni dir . Dopo aver rinominato corrente , se multilogvede num o più vecchi file di registro, rimuove il vecchio file di registro con il timestamp più piccolo. num deve essere almeno 2. Il numero predefinito di file di registro è 10.

L'azione...

 !processor

... imposta un processore per le successive azioni dir . multilogalimenterà la corrente attraverso il processore e salverà l'output come un vecchio file di registro anziché corrente . multilogsalverà anche qualsiasi output scritto dal processore nel descrittore 5 e renderà leggibile l'output sul descrittore 4 quando esegue il processore sul file di registro successivo. Per affidabilità, il processore deve uscire da zero se ha problemi a creare il suo output; multiloglo eseguirà di nuovo. Si noti che il processore in esecuzione può bloccare qualsiasi input di alimentazione del programma multilog.


2

Il meglio che ho trovato per quanto riguarda un'approssimazione che non comporta la scrittura di enormi pezzi di codice è questo zshcodice:

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

Qui suddividendo e ruotando in al massimo 51 file di grandi dimensioni da 20 MiB.


forse ... loopmounts? btrfspuò anche essere montato con compress-force=zlib.
Mikeserv,

2

Ecco uno script python hackerato per fare qualcosa di simile a quello che stai richiedendo:

#!/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
C'è un motivo per averlo come uno script di shell che execè Python come la prima cosa invece di usare il hashbang pythono env python?
peterph,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.