Die beste Möglichkeit, einem Protokoll zu folgen und einen Befehl auszuführen, wenn Text im Protokoll angezeigt wird

54

Ich habe ein Serverprotokoll, das eine bestimmte Textzeile in die Protokolldatei ausgibt, wenn der Server in Betrieb ist. Ich möchte einen Befehl ausführen, sobald der Server hochgefahren ist, und daher etwa Folgendes tun:

tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?

Wie geht das am besten?

jonderry
quelle
3
Verwenden Sie diese Option, um tail -Fdie Protokollrotation zu handhaben - dh sie my.logwird voll und wechselt zu my.log.1und Ihr Prozess erstellt eine neuemy.log
sg

Antworten:

34

Ein einfacher Weg wäre awk.

tail -f /path/to/serverLog | awk '
                    /Printer is on fire!/ { system("shutdown -h now") }
                    /new USB high speed/  { system("echo \"New USB\" | mail admin") }'

Und ja, beide sind echte Nachrichten aus einem Kernel-Log. Perl ist dafür möglicherweise etwas eleganter und kann auch die Notwendigkeit für Tail ersetzen. Wenn Sie Perl verwenden, sieht es ungefähr so ​​aus:

open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
    if(eof $fd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Printer is on fire!/) {
        system("shutdown -h now");
    } elsif($line =~ /new USB high speed/) {
        system("echo \"New USB\" | mail admin");
    }
}
penguin359
quelle
Mir gefällt die awkLösung, kurz und einfach in einer Zeile zu arbeiten. Wenn der Befehl, den ich ausführen möchte, jedoch Anführungszeichen enthält, kann dies etwas umständlich sein. Gibt es eine Alternative, etwa die Verwendung von Pipelines und zusammengesetzten Befehlen, die auch eine kurze einzeilige Lösung ermöglicht, für die der resultierende Befehl jedoch nicht übergeben werden muss? als Zeichenfolge?
Jonderry
1
@jon Du kannst awk als Skript schreiben. Verwenden Sie "#! / Usr / bin awk -f" als erste Zeile des Skripts. Dadurch werden die äußeren einfachen Anführungszeichen in meinem Beispiel nicht mehr benötigt und für die Verwendung innerhalb eines system()Befehls freigegeben.
Pinguin359
@ penguin359, Stimmt, aber es wäre immer noch interessant, dies auch über die Befehlszeile zu tun. In meinem Fall gibt es eine Vielzahl verschiedener Dinge, die ich tun möchte, einschließlich vieler Dinge, die ich nicht vorhersehen kann. Daher ist es praktisch, den Server einfach zu starten und alles in einer Zeile zu erledigen.
Jonderry
Ich fand eine Alternative, obwohl ich nicht weiß, wie solide es ist:tail -f /path/to/serverLog | grep "server is up" | head -1 && do_some_command
jonderry
@jon Das scheint ein bisschen zerbrechlich, wenn man den Kopf so benutzt. Noch wichtiger ist, dass es nicht wie meine Beispiele wiederholbar ist. Befindet sich "server is up" in den letzten zehn Zeilen des Protokolls, wird der Befehl ausgelöst und sofort beendet. Wenn Sie es neu starten, wird es höchstwahrscheinlich ausgelöst und wieder beendet, es sei denn, dem Protokoll wurden zehn Zeilen hinzugefügt, die nicht "server is up" enthalten. Eine Modifikation, die möglicherweise besser funktioniert, ist, tail -n 0 -f /path/to/serverLog dass die letzten 0 Zeilen der Datei gelesen werden und dann darauf gewartet wird, dass weitere Zeilen gedruckt werden.
Pinguin359
16

Wenn Sie nur eine Möglichkeit suchen und hauptsächlich in der Shell bleiben möchten, anstatt awkoder zu verwenden perl, können Sie Folgendes tun:

tail -F /path/to/serverLog | 
grep --line-buffered 'server is up' | 
while read ; do my_command ; done

... der my_commandjedes Mal ausgeführt wird, wenn " Server in Betrieb " in der Protokolldatei angezeigt wird. Für mehrere Möglichkeiten könnten Sie vielleicht die fallen lassen grepund stattdessen eine caseinnerhalb der verwenden while.

Die Hauptstadt -Fteilt tailmit, auf die Protokolldatei zu achten, die gedreht werden soll. dh wenn die aktuelle Datei umbenannt wird und eine andere Datei mit dem gleichen Namen ihren Platz einnimmt, tailwird auf die neue Datei umgeschaltet.

Die --line-bufferedOption teilt grepmit, dass der Puffer nach jeder Zeile geleert werden soll. Andernfalls kann my_commandmöglicherweise nicht rechtzeitig erreicht werden (vorausgesetzt, die Protokolle haben eine angemessene Zeilengröße).

Jander
quelle
2
Diese Antwort gefällt mir sehr gut, hat aber anfangs bei mir nicht funktioniert. Ich denke, Sie müssen die --line-bufferedOption hinzufügen grepoder auf andere Weise sicherstellen, dass die Ausgabe zwischen den Zeilen gespült wird. Andernfalls hängt sie nur und my_commandwird nie erreicht. Wenn Sie es vorziehen ack, hat es eine --flushFlagge; Wenn Sie es vorziehen ag, versuchen Sie es mit stdbuf. stackoverflow.com/questions/28982518/…
Doctaphred
Ich habe es mit versucht do exit ;. Es schien gut zu funktionieren, aber der Schwanz war nie fertig und unser Drehbuch ging nie in die nächste Zeile über. Gibt es eine Möglichkeit, den Schwanz in der doSektion zu stoppen ?
Machtyn
14

Diese Frage scheint bereits beantwortet zu sein, aber ich denke, es gibt eine bessere Lösung.

Stattdessen tail | whateverdenke ich, was Sie wirklich wollen, ist swatch. Swatch ist ein Programm, das speziell dafür entwickelt wurde, Ihre Anforderungen zu erfüllen, eine Protokolldatei zu überwachen und Aktionen basierend auf Protokollzeilen auszuführen. Für die Verwendung ist tail|fooes erforderlich, dass ein Terminal aktiv ist, um dies zu tun. Swatch hingegen wird als Daemon ausgeführt und überwacht immer Ihre Protokolle. Swatch ist in allen Linux-Distributionen verfügbar.

Ich ermutige Sie, es auszuprobieren. Sie können zwar einen Nagel mit der Rückseite eines Schraubenziehers einschlagen, dies bedeutet jedoch nicht, dass Sie dies tun sollten.

Das beste 30-Sekunden-Tutorial zu Swatch, das ich finden konnte, finden Sie hier: http://www.campin.net/newlogcheck.html

Bahamat
quelle
1
Das Tutorial ist jetzt 404: /
dort
10

Es ist seltsam, dass niemand etwas über multitailHilfsprogramme erwähnt hat, die diese Funktionalität bereits enthalten. Ein Anwendungsbeispiel:

Zeigen Sie die Ausgabe eines Ping-Befehls an und senden Sie eine Nachricht an alle aktuell angemeldeten Benutzer, wenn ein Timeout angezeigt wird

multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"

Siehe auch weitere Beispiele von multitailNutzung.

PHP-Codierer
quelle
+1, ich hatte keine Ahnung, `Multitail hatte diese Art von Ninja-Fähigkeiten versteckt. Vielen Dank für den Hinweis.
Caleb
8

konnte den Job alleine machen

Lassen Sie uns sehen, wie einfach und lesbar es sein könnte:

mylog() {
    echo >>/path/to/myscriptLog "$@"
}

while read line;do
    case "$line" in
        *"Printer on fire"* )
            mylog Halting immediately
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Incomming or refresh for ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "untrapped entry: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

Wenn Sie Bashs nicht verwenden regex, kann dies sehr schnell gehen!

Aber + ist ein sehr effizientes und interessantes Tandem

Aber für Server mit hoher Auslastung, und da ich es mag, sedweil es sehr schnell und sehr skalierbar ist, verwende ich oft Folgendes:

while read event target lost ; do
    case $event in
        NEW )
            ip2int $target intTarget
            ((count[intTarget]++))
        ...

    esac
done < <(tail -f /path/logfile | sed -une '
  s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
  s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
  ...
')
F. Hauri
quelle
6

So habe ich auch angefangen, bin aber viel raffinierter geworden. Ein paar Dinge, mit denen man sich befassen sollte:

  1. Wenn das Ende des Protokolls bereits "Server ist in Betrieb" enthält.
  2. Beendet den Tail-Prozess automatisch, sobald er gefunden wurde.

Ich benutze etwas in diesem Sinne:

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # You can put code here if you want to do something
  # once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}

Es funktioniert so lange, tailbis die ${RELEASE}Datei Daten enthält.

Sobald das grepgelingt:

  1. Schreibt die Ausgabe auf ${RELEASE}den Willen
  2. Beenden Sie den ${wait_pid}Vorgang bis
  3. Verlasse das tail

Hinweis: Sie sedkönnen die Anzahl der Zeilen tail, die beim Start erzeugt werden, genauer bestimmen und diese Anzahl entfernen. Aber im Allgemeinen ist es 10.

nix
quelle