Przechowuj dane wyjściowe polecenia w buforze pierścieniowym

16

Mam długo działające polecenie, które generuje dużo danych wyjściowych na standardowym wyjściu. Chciałbym być w stanie zatrzymać na przykład tylko ostatnie trzy dni lub ostatni gibibajt (unikając wycinania linii na środku) i, jeśli to możliwe, w kawałkach plików nie większych niż 20 MiB. Każda porcja pliku jest nazywana sufiksem numerycznym lub znacznikiem czasu.

Coś jak:

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

Napisałby:

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

Gdy osiągnie 20 mln, skompresuje go i otworzy nowy, i tak dalej, a po pewnym czasie zacznie usuwać najstarsze pliki.

Czy takie polecenie istnieje?

Zdaję sobie sprawę z logrotatejego możliwości zarządzania plikami napisanymi przez inne aplikacje, ale szukam czegoś prostszego, co nie wymaga konfigurowania zadania cron, określania reguł, zawieszania procesu itp.

Stéphane Chazelas
źródło
Co to jest „gibibajt”?
Peter Mortensen
@PeterMortensen Wikipedia: Gibibyte
jw013

Odpowiedzi:

6

Możesz uzyskać część tego, co chcesz, za pomocą pipelogu , który „pozwala na obracanie lub czyszczenie dziennika uruchomionego procesu przez przesłanie go przez półprodukt, który reaguje na sygnały zewnętrzne”, np .:

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

Następnie możesz uzyskać pid /tmp/spewpipe.pidi:

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

Ale musiałbyś założyć crona lub coś takiego. Jest jednak jeden haczyk. Uwaga I gzip spew.log.1- dzieje się tak, ponieważ -xpolecenie jest wykonywane po obróceniu dziennika. Masz więc kolejny problem z nadpisywaniem za spew.log.1.gzkażdym razem, chyba że napiszesz krótki skrypt, aby wykonać gzip i przenieść plik później, i użyć go jako -xpolecenia.

Pełne ujawnienie: Napisałem to, więc oczywiście działa idealnie . ;) Będę miał na uwadze opcję kompresji lub coś, co lepiej to ułatwi, dla wersji 0.2 (zamierzony cel -xjest nieco inny, ale będzie działał jak wyżej). Również automatyczny rollover jest dobrym pomysłem ... pierwsza wersja jest celowo minimalna, ponieważ oparłem się pokusie dodania funkcji, które nie były konieczne (w końcu nie jest tak trudno ustawić zadanie crona).

Zauważ, że jest przeznaczony do wyświetlania tekstu ; jeśli istnieją potencjalne bajty zerowe, powinieneś użyć -z- który zamienia zero na coś innego. Był to kompromis w celu uproszczenia wdrożenia.

Złotowłosa
źródło
Dzięki. Nie mogę się doczekać pipelog-0.3;-). Natknąłem się również na metacpan.org/release/File-Write-Rotate . Zauważ, że zadania cron nie pomogą wiele w rotacji w zależności od rozmiaru pliku.
Stéphane Chazelas
Obracanie w zależności od rozmiaru!?! Utrzymuje to, że dane wyjściowe są opróżniane, dzięki czemu można statystycznie
rejestrować
Nie można było w ten sposób utrzymać rozmiaru poniżej 20 mln (jak w moich wymaganiach dotyczących pytania) w ten sposób.
Stéphane Chazelas
Drugą rzeczą jest to, że jest to tylko tekst (dodałem ostatni akapit na ten temat).
goldilocks
4

Multilog Dana Bernsteina najwyraźniej może to zrobić - a może większość, zapewniając jednocześnie ujście za pomocą deskryptorów plików do procesora!, Aby nadrobić różnicę, jak chcesz - chociaż specyfikacje rozmiaru 20M / 1G mogą wymagać nieco finaglingu, ponieważ wydaje się, że 16M jest jego poza limitem na dziennik. Poniżej znajduje się w większości wybór kopiuj i wklej z powyższego linku, chociaż link zawiera również inne opcje, takie jak znacznik czasu dla linii, utrzymywanie [innych] plików zawierających tylko najnowszy wzorzec dopasowania linii i więcej .

Berło

 multilog script

... skrypt składa się z dowolnej liczby argumentów. Każdy argument określa jedno działanie. Działania są wykonywane w kolejności dla każdego wiersza danych wejściowych.

Wybieranie linii

Każda linia jest początkowo wybrana. Akcja...

-pattern

... odznacza linię, jeśli wzór pasuje do linii. Akcja...

+pattern

wybiera linię, jeśli wzór pasuje do linii.

... wzór jest ciągiem gwiazd i innych gwiazd. Pasuje do dowolnego łączenia łańcuchów pasujących do wszystkich gwiazd i innych gwiazd w tej samej kolejności. Non-star pasuje do siebie. Gwiazdka przed końcem wzoru pasuje do dowolnego ciągu, który nie zawiera następnego znaku we wzorze. Gwiazdka na końcu wzoru pasuje do dowolnego łańcucha.

Automatycznie obracane dzienniki

Jeśli katalog zaczyna się od kropki lub ukośnika, akcja ...

 dir

... dołącza każdą wybraną linię do dziennika o nazwie reż . Jeśli katalog nie istnieje, multilogtworzy go.

Format dziennika jest następujący:

  1. reż to katalog zawierający pewną liczbę starych plików dziennika, plik dziennika o nazwie prąd i inne pliki dla multilogśledzić jego działania.

  2. Każdy stary plik dziennika ma nazwę zaczynającą się od @ , kontynuującą dokładną sygnaturę czasową pokazującą, kiedy plik został ukończony, i kończącą się jednym z następujących kodów:

    • .s : ten plik jest całkowicie przetworzony i bezpiecznie zapisany na dysku.
    • .u : Ten plik został utworzony w momencie awarii. Być może został obcięty. Nie został przetworzony.

Akcja...

 ssize

... ustawia maksymalny rozmiar pliku dla kolejnych reż działań. multilogzdecyduje, że prąd jest wystarczająco duży, jeśli prąd ma bajty wielkości . ( multilogzdecyduje również, że prąd jest wystarczająco duży, jeśli zobaczy nową linię w granicach 2000 bajtów od maksymalnego rozmiaru pliku; próbuje zakończyć pliki dziennika na granicach linii). Rozmiar musi wynosić od 4096 do 16777215. Domyślny maksymalny rozmiar pliku to 99999.

W wersjach 0.75 i nowszych: Jeśli multilogodbiera sygnał ALRM , natychmiast decyduje, że prąd jest wystarczająco duży, jeśli prąd nie jest pusty.

(Uwaga: podejrzewam, że zsh schedulemożna łatwo przekonać wbudowanego do wysyłania ALRMw określonych odstępach czasu w razie potrzeby.)

Akcja...

 nnum

... ustawia liczbę plików dziennika dla kolejnych reż działań. Po zmianie nazwy bieżącego , jeśli multilogwidzi num lub więcej starych plików dziennika, usuwa stary plik dziennika z najmniejszym znacznikiem czasu. liczba musi wynosić co najmniej 2. Domyślna liczba plików dziennika to 10.

Akcja...

 !processor

... ustawia procesor do kolejnych reż działań. multilogpoda prąd przez procesor i zapisze dane wyjściowe jako stary plik dziennika zamiast bieżącego . multilogzapisze również dane wyjściowe, które procesor zapisuje do deskryptora 5, i sprawi, że dane wyjściowe będą czytelne na deskryptorze 4, gdy uruchomi procesor na następnym pliku dziennika. Aby zapewnić niezawodność, procesor musi wyjść z niezerowej wartości, jeśli ma problemy z tworzeniem danych wyjściowych; multiloguruchomi go ponownie. Zauważ, że działający procesor może blokować każdy sygnał wejściowy programu multilog.

mikeserv
źródło
2

Najlepsze, jakie do tej pory znalazłem jako przybliżenie, które nie wymaga pisania dużych fragmentów kodu, to następujący zshkod:

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

Tutaj dzielenie i obracanie do maksymalnie 51 dużych plików 20 MB.

Stéphane Chazelas
źródło
może ... pętle? btrfsmożna również zamontować za pomocą compress-force=zlib.
mikeserv
2

Oto zhakowany skrypt Pythona, aby zrobić coś takiego, o co prosisz:

#!/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)
Mark Wagner
źródło
1
Czy istnieje powód, aby używać go jako skryptu powłoki, który execpython jest pierwszą rzeczą zamiast używania skrótu pythonlub env python?
peterph