wake-up-neo.net

Wie kann ich eine Protokolldatei in Python erstellen?

Ich möchte die Ausgabe von tail -F oder etwas Ähnlichem in Python für mich verfügbar machen, ohne zu blockieren oder zu sperren. Ich habe einen wirklich alten Code gefunden, um das zu tun hier , aber ich denke, es muss einen besseren Weg geben oder eine Bibliothek, um dasselbe zu tun. Kennt jemand einen?

Im Idealfall hätte ich etwas wie tail.getNewData(), das ich jedes Mal anrufen konnte, wenn ich mehr Daten wollte.

60
Eli

Das kommt ziemlich spät, aber ich habe wieder das gleiche Problem, und es gibt jetzt eine viel bessere Lösung. Verwenden Sie einfach pygtail :

Pygtail liest Protokolldateizeilen, die nicht gelesen wurden. Es wird sogar behandeln Protokolldateien, die gedreht wurden. Basierend auf logcheck's logtail2 ( http://logcheck.org )

12
Eli

Nicht blockierend

Wenn Sie mit Linux arbeiten (Windows unterstützt das Aufrufen von Select für Dateien nicht), können Sie das Subprozess-Modul zusammen mit dem Select-Modul verwenden.

import time
import subprocess
import select

f = subprocess.Popen(['tail','-F',filename],\
        stdout=subprocess.PIPE,stderr=subprocess.PIPE)
p = select.poll()
p.register(f.stdout)

while True:
    if p.poll(1):
        print f.stdout.readline()
    time.sleep(1)

Dadurch wird die Ausgabepipe nach neuen Daten abgefragt und gedruckt, wenn sie verfügbar ist. Normalerweise würden time.sleep(1) und print f.stdout.readline() durch nützlichen Code ersetzt.

Blockierung

Sie können das Subprozess-Modul ohne die zusätzlichen Select-Modul-Aufrufe verwenden.

import subprocess
f = subprocess.Popen(['tail','-F',filename],\
        stdout=subprocess.PIPE,stderr=subprocess.PIPE)
while True:
    line = f.stdout.readline()
    print line

Dadurch werden auch neue Zeilen gedruckt, wenn sie hinzugefügt werden. Sie werden jedoch blockiert, bis das Tail-Programm geschlossen wird, wahrscheinlich mit f.kill().

57
Matt

Verwenden des sh Moduls (pip install sh):

from sh import tail
# runs forever
for line in tail("-f", "/var/log/some_log_file.log", _iter=True):
    print(line)

[aktualisieren]

Da sh.tail mit _iter = True ein Generator ist, können Sie:

import sh
tail = sh.tail("-f", "/var/log/some_log_file.log", _iter=True)

Dann können Sie "getNewData" mit:

new_data = tail.next()

Wenn der Endpuffer leer ist, wird dieser blockiert, bis weitere Daten vorhanden sind (aus Ihrer Frage ist nicht klar, was Sie in diesem Fall tun möchten).

[aktualisieren]

Dies funktioniert, wenn Sie -f durch -F ersetzen, aber in Python würde es sperren. Ich wäre mehr daran interessiert, eine Funktion zu haben, die ich anrufen könnte, um neue Daten zu erhalten, wenn ich es möchte, falls dies möglich ist. - Eli

Ein Container-Generator, der den Abrufaufruf innerhalb einer while-Schleife ausführt und eventuelle E/A-Ausnahmen abfängt, hat fast die gleiche Wirkung wie -F.

def tail_F(some_file):
    while True:
        try:
            for line in sh.tail("-f", some_file, _iter=True):
                yield line
        except sh.ErrorReturnCode_1:
            yield None

Wenn auf die Datei nicht mehr zugegriffen werden kann, gibt der Generator None zurück. Es wird jedoch weiterhin blockiert, bis neue Daten vorhanden sind, wenn auf die Datei zugegriffen werden kann. Es bleibt mir unklar, was Sie in diesem Fall tun möchten.

Raymond Hettinger Ansatz scheint ziemlich gut zu sein:

def tail_F(some_file):
    first_call = True
    while True:
        try:
            with open(some_file) as input:
                if first_call:
                    input.seek(0, 2)
                    first_call = False
                latest_data = input.read()
                while True:
                    if '\n' not in latest_data:
                        latest_data += input.read()
                        if '\n' not in latest_data:
                            yield ''
                            if not os.path.isfile(some_file):
                                break
                            continue
                    latest_lines = latest_data.split('\n')
                    if latest_data[-1] != '\n':
                        latest_data = latest_lines[-1]
                    else:
                        latest_data = input.read()
                    for line in latest_lines[:-1]:
                        yield line + '\n'
        except IOError:
            yield ''

Dieser Generator gibt '' zurück, wenn auf die Datei nicht mehr zugegriffen werden kann oder wenn keine neuen Daten vorhanden sind.

[aktualisieren]

Die vorletzte Antwort umkreist den Anfang der Datei, sobald die Daten ausgehen. - Eli 

Ich denke, der zweite wird die letzten zehn Zeilen ausgeben, wenn der Tail-Prozess endet. Dies ist bei -f der Fall, wenn ein E/A-Fehler vorliegt. Das tail --follow --retry-Verhalten ist für die meisten Fälle, die mir in Unix-ähnlichen Umgebungen einfallen, nicht weit.

Wenn Sie Ihre Frage aktualisieren, um zu erklären, was Ihr eigentliches Ziel ist (der Grund, warum Sie den Schwanz nachahmen möchten), erhalten Sie möglicherweise eine bessere Antwort.

Die letzte Antwort folgt nicht wirklich dem Schwanz und liest lediglich, was zur Laufzeit verfügbar ist. - Eli

Natürlich zeigt tail standardmäßig die letzten 10 Zeilen an ... Sie können den Dateizeiger mit file.seek am Ende der Datei positionieren. Dem Leser wird eine korrekte Implementierung als Übung überlassen.

IMHO ist der Ansatz von file.read () weitaus eleganter als eine auf einem Teilprozess basierende Lösung.

32
Paulo Scardine

Der einzige portable Weg zu tail -f, den eine Datei tatsächlich zu lesen scheint, ist das Lesen und Wiederholen (nach einer sleep), wenn read 0 zurückgibt. Die tail Dienstprogramme auf verschiedenen Plattformen verwenden plattformspezifische Tricks (z. B. kqueue on BSD), um eine Datei für immer effizient auszustatten, ohne dass sleep erforderlich ist.

Daher ist die Implementierung eines guten tail -f in Python wahrscheinlich keine gute Idee, da Sie die Implementierung mit dem kleinsten gemeinsamen Nenner verwenden müssen (ohne auf plattformspezifische Hacks zurückzugreifen). Mit einer einfachen subprocess zum Öffnen von tail -f und dem Durchlaufen der Zeilen in einem separaten Thread können Sie leicht eine nicht blockierende tail -Operation in Python implementieren.

Beispielimplementierung:

import threading, Queue, subprocess
tailq = Queue.Queue(maxsize=10) # buffer at most 100 lines

def tail_forever(fn):
    p = subprocess.Popen(["tail", "-f", fn], stdout=subprocess.PIPE)
    while 1:
        line = p.stdout.readline()
        tailq.put(line)
        if not line:
            break

threading.Thread(target=tail_forever, args=(fn,)).start()

print tailq.get() # blocks
print tailq.get_nowait() # throws Queue.Empty if there are no lines to read
22
nneonneo

Im Idealfall hätte ich so etwas wie tail.getNewData (), das ich jedes Mal aufrufen konnte, wenn ich mehr Daten wollte

Wir haben schon eins und itsa sehr schön. Rufen Sie einfach f.read () an, wenn Sie mehr Daten wünschen. Es beginnt zu lesen, wo der vorherige Lesevorgang unterbrochen wurde, und liest das Ende des Datenstroms durch:

f = open('somefile.log')
p = 0
while True:
    f.seek(p)
    latest_data = f.read()
    p = f.tell()
    if latest_data:
        print latest_data
        print str(p).center(10).center(80, '=')

Verwenden Sie zum zeilenweisen Lesen f.readline (). Manchmal endet die gelesene Datei mit einer teilweise gelesenen Zeile. Behandeln Sie diesen Fall mit f.tell (), wobei Sie die aktuelle Dateiposition finden, und verwenden Sie f.seek (), um den Dateizeiger zurück an den Anfang der unvollständigen Zeile zu verschieben. Siehe dieses ActiveState-Rezept für Funktionscode.

9

Sie können die Tailer-Bibliothek verwenden: https://pypi.python.org/pypi/tailer/

Es gibt eine Option, um die letzten Zeilen zu erhalten:

# Get the last 3 lines of the file
tailer.tail(open('test.txt'), 3)
# ['Line 9', 'Line 10', 'Line 11']

Und es kann auch eine Datei folgen:

# Follow the file as it grows
for line in tailer.follow(open('test.txt')):
    print line

Wenn man schwanzartiges Verhalten will, scheint das eine gute Option zu sein.

4
Haroldo_OK

Eine weitere Option ist die Bibliothek tailhead , die beide Python-Versionen der Dienstprogramme tail und head sowie die API enthält, die in Ihrem eigenen Modul verwendet werden können.

Ursprünglich auf dem tailer-Modul basiert, besteht sein Hauptvorteil darin, Dateien nach Pfad zu verfolgen, d. H. Sie können Situationen behandeln, wenn eine Datei neu erstellt wird. Außerdem gibt es einige Bugfixes für verschiedene Edge-Fälle.

2
Kentzo

Alle Antworten, die tail -f verwenden, sind nicht Pythonic.

Hier ist der Pythonic-Weg: (ohne externes Werkzeug oder Bibliothek)

def follow(thefile):
     while True:
        line = thefile.readline()
        if not line or not line.endswith('\n'):
            time.sleep(0.1)
            continue
        yield line



if __== '__main__':
    logfile = open("run/foo/access-log","r")
    loglines = follow(logfile)
    for line in loglines:
        print(line, end='')
2
Ijaz Ahmad Khan

Wenn Sie Ijaz Ahmad Khans answer anpassen, um Zeilen nur dann zu ergeben, wenn sie vollständig geschrieben sind (Zeilen enden mit einem Zeilenumbruchzeichen), ergibt sich eine Pythonic-Lösung ohne externe Abhängigkeiten:

def follow(file) -> Iterator[str]:
    """ Yield each line from a file as they are written. """
    line = ''
    while True:
        tmp = file.readline()
        if tmp is not None:
            line += tmp
            if line.endswith("\n"):
                yield line
                line = ''
        else:
            time.sleep(0.1)


if __== '__main__':
    for line in follow(open("test.txt", 'r')):
        print(line, end='')
2
Isaac Turner

Sie können auch den Befehl 'AWK' verwenden.
Weitere Informationen finden Sie unter: http://www.unix.com/Shell-programming-scripting/41734-how-print-specific-lines-awk.html
awk kann verwendet werden, um die letzte Zeile, die letzten Zeilen oder eine Zeile in einer Datei zu beenden.
Dies kann von Python aus aufgerufen werden.

0
Madhusoodan

Wenn Sie Linux verwenden, implementieren Sie eine nicht blockierende Implementierung in Python auf folgende Weise.

import subprocess
subprocess.call('xterm -title log -hold -e \"tail -f filename\"&', Shell=True, executable='/bin/csh')
print "Done"
0
Anand Satya