Anhängen eines Zeitstempelstempels zusammen mit Protokolldateizeilen

11

Ich habe eine Protokolldatei und muss Zeitstempel an jede Zeile anhängen, wenn sie hinzugefügt werden. Daher suche ich nach einem Skript, das Zeitstempel an jeden Eintrag in Protokollzeilen anfügt und als Cron-Job ausgeführt werden kann.

Kratos
quelle

Antworten:

18

Der allgemeine Weg

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

Wie es funktioniert:

  1. catLiest die aufgerufene Datei input.logund druckt sie einfach in den Standardausgabestream.

    Normalerweise ist die Standardausgabe mit einem Terminal verbunden, aber dieses kleine Skript enthält, |sodass die Shell die Standardausgabe von catauf die Standardeingabe von umleitet sed.

  2. sedLiest Daten (wie sie caterzeugt werden), verarbeitet sie (gemäß dem mit der -eOption gelieferten Skript ) und druckt sie dann in ihrer Standardausgabe aus. Das Skript "s/^/$(date -R) /"bedeutet, dass jeder Zeilenanfang durch einen vom date -RBefehl generierten Text ersetzt wird (die allgemeine Konstruktion für den Befehl "Ersetzen" lautet :) s/pattern/replace/.

  3. Dann wird laut >> bashRedirects die Ausgabe sedin eine Datei mit dem Namen output.log( >bedeutet Ersetzen des Dateiinhalts und >>Anhängen an das Ende).

Das Problem wird $(date -R)einmal ausgewertet, wenn Sie das Skript ausführen, sodass der aktuelle Zeitstempel am Anfang jeder Zeile eingefügt wird. Der aktuelle Zeitstempel ist möglicherweise weit von einem Moment entfernt, in dem eine Nachricht generiert wurde. Um dies zu vermeiden, müssen Sie Nachrichten so verarbeiten, wie sie in die Datei geschrieben werden, nicht mit einem Cron-Job.

FIFO

Die oben beschriebene Standard-Stream-Umleitung wird als Pipe bezeichnet . Sie können es nicht nur |zwischen Befehlen im Skript umleiten , sondern auch über eine FIFO-Datei (auch bekannt als Named Pipe ). Ein Programm schreibt in die Datei und ein anderes liest Daten und empfängt sie beim ersten Senden.

Wählen Sie ein Beispiel:

$ 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

Wie es funktioniert:

  1. mkfifo erstellt eine Named Pipe

  2. while true; do sed ... ; doneführt eine Endlosschleife aus und wird bei jeder Iteration sedmit Umleitung foo.log.fifozu ihrer Standardeingabe ausgeführt; sed Blockiert das Warten auf Eingabedaten und verarbeitet dann eine empfangene Nachricht und druckt sie in die Standardausgabe, die an umgeleitet wird foo.log.

    Zu diesem Zeitpunkt müssen Sie ein neues Terminalfenster öffnen, da die Schleife das aktuelle Terminal belegt.

  3. echo ... > foo.log.fifodruckt eine Nachricht in ihre Standardausgabe, die in die FIFO-Datei umgeleitet wird, sedempfängt sie und verarbeitet und schreibt in eine reguläre Datei.

Der wichtige Hinweis ist das Fifo, genau wie jedes andere Rohr keinen Sinn hat, wenn eine seiner Seiten mit keinem Prozess verbunden ist. Wenn Sie versuchen, in eine Pipe zu schreiben, wird der aktuelle Prozess blockiert, bis jemand Daten auf der anderen Seite der Pipe liest. Wenn Sie aus einer Pipe lesen möchten, wird der Prozess blockiert, bis jemand Daten in die Pipe schreibt. Die sedSchleife im obigen Beispiel macht nichts (schläft), bis Sie dies tun echo.

Für Ihre spezielle Situation konfigurieren Sie Ihre Anwendung einfach so, dass Protokollnachrichten in die FIFO-Datei geschrieben werden. Wenn Sie es nicht konfigurieren können, löschen Sie einfach die ursprüngliche Protokolldatei und erstellen Sie eine FIFO-Datei. Beachten Sie jedoch erneut, dass sedIhr Programm beim Versuch, writeauf die Datei readzuzugreifen , blockiert wird , wenn die Schleife aus irgendeinem Grund stirbt, bis jemand vom FIFO kommt.

Der Vorteil ist der aktuelle Zeitstempel, der ausgewertet und an eine Nachricht angehängt wird, während das Programm sie in die Datei schreibt.

Asynchrone Verarbeitung mit tailf

Um das Schreiben in das Protokoll und die Verarbeitung unabhängiger zu gestalten, können Sie zwei reguläre Dateien mit verwenden tailf. Eine Anwendung schreibt eine Nachricht in eine Rohdatei und andere Prozesse lesen neue Zeilen (folgen Sie, um asynchron zu schreiben) und verarbeiten Daten mit Schreiben in die zweite Datei.

Nehmen wir ein Beispiel:

# 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

Wie es funktioniert:

  1. Führen Sie den tailfProzess aus, der den Schreibvorgängen folgt, bar.raw.logund drucken Sie sie in die Standardausgabe, die in die Endlosschleife umgeleitet while read ... echowird. Diese Schleife führt zwei Aktionen aus: Lesen von Daten von der Standardeingabe in eine aufgerufene linePuffervariable und Schreiben des generierten Zeitstempels mit den folgenden gepufferten Daten in die bar.log.

  2. Schreiben Sie einige Nachrichten an die bar.raw.log. Sie müssen dies in einem separaten Terminalfenster tun, da das erste Fenster belegt ist, tailfdas den Schreibvorgängen folgt und seine Arbeit erledigt. Ziemlich einfach.

Der Vorteil ist, dass Ihre Anwendung nicht blockieren würde, wenn Sie töten tailf. Die Nachteile sind weniger genaue Zeitstempel und das Duplizieren von Protokolldateien.

Dmitry Vasilyanov
quelle
Vielen Dank .Fifo scheint für meine Verwendung sehr geeignet zu sein. Mein Programm schreibt bereits in ein file.txt-Protokoll, aber diese Datei muss zu Ihrer foo.log.fifo umgeleitet werden. Aber $ tailf file.txt> foo.log.fifo hängt ! Wie kann ich meinen Inhalt in einer aktualisierten Protokolldatei auf diese foo.log.fifo umleiten, damit auch die Zeit angehängt werden kann?
Kratos
Nur die Antwort verbessert. Erklärt, warum FIFO Ihre blockiert tailf, und fügte die richtige Verwendung hinzu. Eigentlich tailfscheint der Weg mit eleganter zu sein, aber ich habe den FIFO-Weg mit der Hoffnung verlassen, dass er für jemanden nützlich sein wird.
Dmitry Vasilyanov
Wenn Sie Ihre sed-Antwort zum Kommentieren der vmstat-Ausgabe verwenden, erhalten Sie einen Zeitstempel, der sich nicht ändert: vmstat 1 | sed -e "s / ^ / $ (Datum -R) /"
ChuckCottrill
6

Sie können das tsPerl-Skript verwenden von moreutils:

$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Stéphane Chazelas
quelle
2

Geändert von der Antwort von Dmitry Vasilyanov.

Im Bash-Skript können Sie die Ausgabe zeilenweise umleiten und mit Zeitstempel umbrechen.

Wann zu verwenden:

  • Fügen Sie bei Bash-Skript-Jobs die Zeile vor dem Hauptskript ein
  • Erstellen Sie für Jobs ohne Skript ein Skript, um das Programm aufzurufen.
  • Für die Systemsteuerung von Diensten ist es besser, tailfdie Protokolldatei zu verwenden, wie Dmitry Vasilyanov sagte.

Ein Beispiel mit dem Namen 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"

Und das Ergebnis:

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

Wie es funktioniert

  1. exec &> Leiten Sie stdout und stderr an dieselbe Stelle um
  2. >( ... ) Pipe-Ausgaben an einen asynchronen inneren Befehl
  3. Der Rest funktioniert, als Dmitry Vasilyanov expandierte.

Beispielsweise:

  • Pipe-Zeitstempel und Protokoll in Datei

    #!/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
    
  • Oder drucken Sie den Zeitstempel und melden Sie sich bei stdout an

    #!/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
    

    Speichern Sie sie dann in der /etc/crontabEinstellung

    * * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
    
Jethro Yu
quelle
1

Auf tsdiese Weise habe ich einen Eintrag mit einem Zeitstempel in einem Fehlerprotokoll für ein Skript erhalten, mit dem Cacti mit Statistiken eines Remote-Hosts gefüllt wird.

Um Kakteen zu testen rand, füge ich einige zufällige Werte hinzu, die ich für Temperaturdiagramme zur Überwachung der Systemtemperatur verwende.

Pushmonstats.sh ist ein Skript, das Systemtemperaturstatistiken meines PCs sammelt und diese an einen Raspberry Pi sendet, auf dem Cacti ausgeführt wird. Vor einiger Zeit steckte das Netzwerk fest. Ich habe nur SSH-Timeouts in meinem Fehlerprotokoll. Leider keine Zeiteinträge in diesem Protokoll. Ich wusste nicht, wie ich einem Protokolleintrag einen Zeitstempel hinzufügen sollte. Nach einigen Recherchen im Internet bin ich auf diesen Beitrag gestoßen, und das habe ich mit gemacht ts.

Um es zu testen, habe ich eine unbekannte Option verwendet rand. Was stderr einen Fehler gab. Um es zu erfassen, leite ich es in eine temporäre Datei um. Dann benutze ich cat, um den Inhalt der Datei anzuzeigen und weiterzuleiten ts, füge ein Zeitformat hinzu, das ich in diesem Beitrag gefunden habe, und protokolliere es schließlich in der Fehlerdatei. Dann lösche ich den Inhalt der temporären Datei, andernfalls erhalte ich doppelte Einträge für denselben Fehler.

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

Dies ergibt Folgendes in meinem Fehlerprotokoll:

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

Vielleicht ist dies keine sehr elegante Art, aber es funktioniert. Ich frage mich, ob es einen eleganteren Ansatz gibt.

Frank
quelle