Wie schreibe ich ein Skript, das nur auf neue Protokolleinträge wirkt?

7

Ich denke, das sollte eine einfache Sache sein, aber es fällt mir schwer, es herauszufinden.

Ich versuche, ein Skript zu schreiben, das eine der Apache-Protokolldateien überwacht und bestimmte Maßnahmen ergreift. Aber wie soll ich die Protokolldatei überwachen?

Jedes Mal, wenn eine neue Zeile in das Protokoll geschrieben wird, möchte ich, dass dieser Eintrag überprüft wird, um festzustellen, ob er mit dem übereinstimmt, wonach ich suche, und wenn ja, passiert x. Wenn ich das manuell mache, habe ich cat oder tail -f verwendet. Ich möchte das Skript nicht alle 30 Sekunden über cron ausführen und das gesamte Protokoll (oder sogar die letzten 5 Zeilen) durchgehen, um herauszufinden, welche dieser Zeilen seit der letzten Ausführung des Skripts neu sind, und dann einige Dinge.

Gibt es eine Möglichkeit, nur den einzelnen neuen Eintrag im Protokoll zu überprüfen?

Karmet
quelle

Antworten:

5

Wenn Sie Ihr Skript über cron ausführen, aber die Datei verwenden logtailoder logtail2lesen, wird vermieden, dass die gesamte Datei jede Minute gelesen wird. Logtail verfolgt, wohin es zuletzt gelesen hat, und springt bei der nächsten Verwendung zu diesem Punkt.

Wenn Sie sofort auf neue Protokollzeilen reagieren möchten, anstatt zwischen den Cron-Aufrufen bis zu 59 Sekunden zu warten, müssen Sie tail -foder eine gleichwertige Option verwenden.

Die Antworten von Janne und Khaled versuchen beide, dieses Problem gut zu lösen.

Ladadadada
quelle
6

Sie können von den bereits vorhandenen Linux - Tools wie machen tail, grepund named pipes. Erstellen Sie zunächst eine Named Pipe (FIFO) mit:

$ mkfifo /tmp/myfifo

Zweitens erstellen Sie ein einfaches Skript, das aus dieser FIFO-Datei liest. Hier ist ein einfaches Beispiel:

#!/bin/bash
pipe=/tmp/myfifo

while true
do
    if read line <$pipe; then
        if [[ "$line" == 'quit' ]]; then
            break
        fi
        echo $line
    fi
done
echo "Reader exiting"

Dieses Skript liest aus der Named Pipe und druckt die Zeile nach stdout, bis das Wort "quit" angezeigt wird. Dies ist nur ein Beispiel, das angepasst werden kann.

Drittens verwenden Sie tail, um neue Zeilen zu lesen, die an die Apache-Protokolldatei angehängt sind, und um die Ausgabe an die Named Pipe umzuleiten.

$ tail -n0 -F /var/log/apache2/access.log | grep some_text > /tmp/myfifo

Die -FOption bedeutet, der Datei mit Namen zu folgen, wodurch sie immun gegen Logrotation sein sollte. Es folgt also immer der gleiche Dateiname. Das -n0bedeutet, keine alte Linie zu bekommen. Das grepist nützlich, um nur die relevanten Zeilen zu lenken.

Mit dieser Lösung benötigen Sie keinen Cron-Job. Führen Sie einfach das oben gezeigte Skript und den Befehl tail aus.

Khaled
quelle
Was ist der Grund dafür, hier eine Named Pipe zu verwenden, anstatt nur die Ausgabe von grep in Ihr Skript zu leiten?
Fizban
Named Pipes sind wie unbenannte Pipes, die mit erstellt |wurden. In einigen Fällen kann es jedoch einfacher und klarer sein, Named Pipes zu verwenden.
Khaled
3

Wenn Sie als Syslog-Daemon haben syslog-ng(wahrscheinlich rsyslogdauch), können Sie das verwenden.

Konfigurieren Sie es einfach, um die Apache-Protokolldatei im Auge zu behalten, oder konfigurieren Sie Apache alternativ so, dass Protokolle mit der CustomLogDirektive und an die Syslog-Funktion gesendet werden logger.

Der Syslog-Daemon verwendet dann den Mustervergleich und führt $ foo aus, wenn eine Übereinstimmung gefunden wird. In syslog-ng können Sie beispielsweise einen Protokolldatei-Hook einrichten und wie folgt filtern:

source apache_log { file("/var/log/apache2/access.log"); };
filter apache_match { match("GET /evilscript.php"); };

Und dann ruft syslog-ng ein externes Skript auf

destination apache_logmatch_script { program("/usr/local/bin/apachematch.pl"); };

Zum Schluss alle zusammen:

log { source(apache_log); filter(apache_match); destination apache_logmatch_script); };

Wenn Sie diese Technik verwenden, erzeugt syslog-ng Ihren Skripthintergrund und wartet darauf, dass neue Inhalte angezeigt werden. Aus diesem Grund müssen Sie Ihre Skripte ändern, um auf die Eingabe von STDIN zu warten. Hier ist ein kurzes Perl-Beispiel:

#!/usr/bin/perl -w

$|=1;
while (<>) {
     printf "Stuff happened, I got this entry: %s!\n", $_;
}

Ich werde mit meiner Antwort nicht tiefer gehen, bis ich weiß, dass Sie diese Technik ausprobieren möchten.

Janne Pikkarainen
quelle
1

Sie können die zuletzt gelesene Zeile aufzeichnen. Lesen Sie bei jedem neuen Lauf von der letzten + 1-Zeile bis zum Ende der Datei und aktualisieren Sie den Datensatz.

Beachten Sie jedoch, dass die Protokolldatei möglicherweise gedreht wird. So sollten Sie beispielsweise die Inode-Nummer aufzeichnen.

fail2ban implementiert die Überwachung von Protokolldateien so, wie Sie es zu wollen scheinen. Vielleicht einen Blick darauf werfen? Entweder können Sie einige Ideen ausleihen oder sie tatsächlich verwenden.

Jonathan
quelle
+1 für fail2ban. Das erste, was ich dachte, als ich die Frage
las
Vielen Dank. fail2ban sieht gut aus und obwohl ich nicht versuche, jemanden zu verbieten, ist die allgemeine Prämisse dieselbe. Aber ... es ist in Python, das ich noch nie benutzt habe und alles andere auf unseren Systemen ist Bash oder Perl.
Karmet
0

Sie könnten diff verwenden. Kopieren Sie die Protokolldatei in eine temporäre Datei.

Nehmen Sie einen Unterschied zwischen der Live-Protokolldatei und der temporären Datei.

Führen Sie den Mustervergleich auf dem resultierenden Diff aus.

Idee aus dem nagios check_log Plugin. Weitere Informationen finden Sie unter http://www.kilala.nl/Sysadmin/index.php?id=715 .

dmourati
quelle
0

Wenn fail2ban nicht für Ihre Anforderungen geeignet ist, können Sie versuchen, flog.c zu ändern , das von Markus J. Ranum geschrieben wurde:

"Dieses kleine Dienstprogramm entspricht" tail -f ", ist jedoch intelligent genug, um das Ändern von Dateien zu handhaben, wenn ein Protokollrotator die Datei während der Ausführung unter dem Benutzer entfernt. Im Gegensatz zu" tail -f "ist dieses Programm auch klug genug, um zu beenden, wenn sich der Benutzer abmeldet (!) und keine Protokolle für immer an die pty ausspuckt. Es werden keine Anstrengungen in Bezug auf Eleganz oder Leistung unternommen. "

Sie können ändern, um zu überprüfen, ob eine neue Linie Ihrem gewünschten Muster entspricht, und entsprechend handeln.

Adamo
quelle
0
#!/bin/bash

while read line;
do
      echo $line > /tmp/output.log
      mail -s 'Event Msg.' '[email protected]' < /tmp/output.log
done < <(tail -n0 -F /var/adm/messages)
Ramon
quelle
Es wäre hilfreich, wenn Sie Ihrem Code eine Erklärung hinzufügen würden.
BE77Y