Dołączanie znacznika czasu wraz z wierszami pliku dziennika

11

Mam plik dziennika i muszę dodawać znaczniki czasu do każdej linii, gdy zostaną dodane. Szukam więc skryptu, który dołącza znaczniki czasu do każdego wpisu w wierszach dziennika i który mógłby działać jako zadanie cron.

Kratos
źródło

Odpowiedzi:

18

Ogólna droga

$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log

Jak to działa:

  1. catodczytuje nazwany plik input.logi po prostu drukuje go do standardowego strumienia wyjściowego.

    Zwykle standardowe wyjście jest podłączone do terminala, ale ten mały skrypt zawiera, |więc powłoka przekierowuje standardowe wyjście catna standardowe wejście sed.

  2. sedodczytuje dane (jak catje produkuje), przetwarza (zgodnie ze skryptem dostarczonym z -eopcją), a następnie drukuje na standardowe wyjście. Skrypt "s/^/$(date -R) /"oznacza zamianę każdego początku wiersza na tekst generowany przez date -Rkomendę (ogólna konstrukcja komendy replace to:) s/pattern/replace/.

  3. Następnie zgodnie z >> bashprzekierowuje wyjście seddo pliku o nazwie output.log( >oznacza zastąpić zawartość pliku i >>oznacza dopisać na końcu).

Problem jest $(date -R)oceniany raz po uruchomieniu skryptu, dzięki czemu wstawi on aktualny znacznik czasu na początku każdej linii. Aktualny znacznik czasu może być daleki od momentu wygenerowania wiadomości. Aby tego uniknąć, musisz przetwarzać wiadomości zapisywane do pliku, a nie zadanie cron.

FIFO

Standardowe przekierowanie strumienia opisane powyżej zwane potokiem . Możesz przekierować go nie tylko |między poleceniami w skrypcie, ale poprzez plik FIFO ( zwany potokiem ). Jeden program zapisuje do pliku, a inny odczytuje dane i odbiera je przy pierwszej wysyłce.

Wybierz przykład:

$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;

# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo

$ cat foo.log      

Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz

Jak to działa:

  1. mkfifo tworzy nazwaną potok

  2. while true; do sed ... ; doneuruchamia nieskończoną pętlę i przy każdej iteracji biegnie sedz przekierowaniem foo.log.fifona standardowe wejście; sed blokuje oczekiwanie na dane wejściowe, a następnie przetwarza otrzymaną wiadomość i drukuje ją na standardowe wyjście przekierowane na foo.log.

    W tym momencie musisz otworzyć nowe okno terminala, ponieważ pętla zajmuje aktualny terminal.

  3. echo ... > foo.log.fifowypisuje komunikat na standardowe wyjście przekierowany do pliku fifo i sedodbiera go oraz przetwarza i zapisuje do zwykłego pliku.

Ważną uwagą jest fifo, tak jak każda inna rura nie ma sensu, jeśli jedna z jej stron nie jest połączona z żadnym procesem. Jeśli spróbujesz zapisać na potoku, bieżący proces zostanie zablokowany, dopóki ktoś nie przeczyta danych po drugiej stronie potoku. Jeśli chcesz czytać z potoku, proces zostanie zablokowany, dopóki ktoś nie zapisze danych na potoku. sedPętla w powyższym przykładzie nie robi nic (śpi), dopóki nie robić echo.

W konkretnej sytuacji wystarczy skonfigurować aplikację, aby zapisywała komunikaty dziennika w pliku FIFO. Jeśli nie możesz go skonfigurować - po prostu usuń oryginalny plik dziennika i utwórz plik FIFO. Ale zauważ ponownie, że jeśli sedpętla umrze z jakiegoś powodu - twój program zostanie zablokowany przy próbie writeprzejścia do pliku, dopóki ktoś nie zrobi tego readz fifo.

Korzyścią jest ocena bieżącego znacznika czasu i dołączanie go do komunikatu, gdy program zapisuje go do pliku.

Przetwarzanie asynchroniczne z tailf

Aby zapisywanie w dzienniku i przetwarzanie było bardziej niezależne, możesz użyć dwóch zwykłych plików tailf. Aplikacja zapisuje komunikat do nieprzetworzonego pliku, a inny proces odczytuje nowe wiersze (postępuje zgodnie z zapisem asynchronicznym) i przetwarza dane z zapisem do drugiego pliku.

Weźmy przykład:

# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;

$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log

$ cat bar.log

Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz

Jak to działa:

  1. Uruchom tailfproces, który nastąpi po zapisie bar.raw.logi wydrukuj go na standardowe wyjście przekierowane do nieskończonej while read ... echopętli. Ta pętla wykonuje dwie czynności: odczytuje dane ze standardowego wejścia do zmiennej buforowej o nazwie, linea następnie zapisuje wygenerowany znacznik czasu z następującymi buforowanymi danymi do bar.log.

  2. Napisz kilka wiadomości do bar.raw.log. Musisz to zrobić w osobnym oknie terminala, ponieważ zostanie zajęte pierwsze, tailfktóre będzie śledzić zapisy i wykonywać swoją pracę. Całkiem proste.

Zaletą jest to, że Twoja aplikacja nie zablokuje się, jeśli zabijesz tailf. Wady to mniej dokładne znaczniki czasu i powielające się pliki dziennika.

Dmitrij Wasiljanow
źródło
Dziękuję. Fifo wydaje się bardzo przydatne do mojego użytku. Mój program zapisuje już w pliku file.txt, ale ten plik musi zostać przekierowany do twojego foo.log.fifo. Ale robi to $ tailf file.txt> foo.log.fifo zawiesza się ! Jak mogę przekierować moją zawartość w aktualizowanym pliku dziennika do tego pliku foo.log.fifo, aby mógł również dodać czas.
Kratos,
Właśnie poprawiłem odpowiedź. Wyjaśniono, dlaczego fifo blokuje twój tailf, dodałem właściwy sposób korzystania z niego. Właściwie sposób z tailfwydaje się być bardziej elegancki, ale opuściłem fifo z nadzieją, że przyda się komuś.
Dmitrij Wasiljanow
Użycie odpowiedzi sed do opisania wyniku vmstat daje znacznik czasu, który się nie zmienia, vmstat 1 | sed -e "s / ^ / $ (date -R) /"
ChuckCottrill
6

Możesz użyć tsskryptu perla z moreutils:

$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Stéphane Chazelas
źródło
2

Zmodyfikowano z odpowiedzi Dmitrija Wasiljanowa.

W skrypcie bash możesz przekierowywać i owijać dane wyjściowe znacznikiem czasu linia po linii w locie.

Kiedy użyć:

  • W przypadku zadań skryptu bash wstaw wiersz przed skryptem głównym
  • W przypadku zadań innych niż skrypty utwórz skrypt wywołujący program.
  • W przypadku kontroli usług przez system lepiej jest użyć tailfpliku dziennika, jak powiedział Dmitrij Wasiljanow.

Przykład o nazwie foo.sh:

#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)

echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"

A wynik:

$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar

Jak to działa

  1. exec &> Przekieruj stdout i stderr w to samo miejsce
  2. >( ... ) potokuje dane wyjściowe do asynchronicznego polecenia wewnętrznego
  3. Pozostała część działa, gdy Dmitrij Wasilijanow rozwinął się.

Na przykład:

  • znacznik czasu potoku i zaloguj się do pliku

    #!/bin/bash        
    exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
    
    echo "some script commands"
    /path-to/some-thrid-party-programs
    
  • Lub wydrukuj sygnaturę czasową i zaloguj na standardowe wyjście

    #!/bin/bash        
    exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
    
    echo "some script commands"
    /path-to/some-thrid-party-programs
    

    następnie zapisz je w /etc/crontabustawieniach

    * * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
    
Jethro Yu
źródło
1

Użyłem tstego sposobu, aby uzyskać wpis ze znacznikiem czasu w dzienniku błędów skryptu, którego używam do wypełnienia kaktusów statystykami zdalnego hosta.

Aby przetestować kaktusy, używam randdo dodania losowych wartości, których używam do wykresów temperatury do monitorowania temperatury mojego systemu.

Pushmonstats.sh to skrypt, który zbiera statystyki temperatury systemu z mojego komputera i wysyła je do Raspberry Pi, na którym działa Cacti. Jakiś czas temu sieć utknęła. W dzienniku błędów mam tylko przerwy SSH. Niestety, brak wpisów czasu w tym dzienniku. Nie wiedziałem, jak dodać znacznik czasu do wpisu w dzienniku. Po kilku wyszukiwaniach w Internecie natknąłem się na ten post i właśnie tego dokonałem ts.

Aby to przetestować, użyłem nieznanej opcji rand. Co dało błąd stderrowi. Aby go przechwycić, przekierowuję go do pliku tymczasowego. Następnie używam cat, aby wyświetlić zawartość pliku i przesłać do niego potokiem ts, dodać format czasu, który znalazłem w tym poście, i na koniec zalogować go do pliku błędu. Następnie usuwam zawartość pliku tymczasowego, w przeciwnym razie otrzymuję podwójne wpisy dla tego samego błędu.

Crontab:

* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err

To daje następujące informacje w moim dzienniku błędów:

2014-03-22-19:17:53.823720 rand: unknown option -- '-l'

Może nie jest to zbyt elegancki sposób na zrobienie tego, ale działa. Zastanawiam się, czy istnieje bardziej eleganckie podejście.

Szczery
źródło