Get-Tail

Zwar gibt es schon sehr viele Programme, die das Ende einer Datei anzeigen (Siehe auch Tail) und selbst die Powershell hat mit dem Programm "Get-Content" und der Option "-WAIT" ein Hilfsmittel, um Dateien zeilenweise einzulesen und auf neu hinzugefügte Daten zu reagieren. Aber diese Tools sind doch entweder für den Admin oder interaktiven Gebrauch und weniger für eine automatische Verarbeitung neuer Daten. "GET-CONTENT -WAIT" z.B. liest trotzdem immer erst einmal die ganze Datei, ehe es auf mehr Input wartet.

Anforderungen

Daher überlege ich schon lange, ob ich mit eine eigene TAIL-Klasse bzw. TAIL-Objekt erstelle, welche für mich essentielle Funktionen mitbringt. Es reicht mir nicht aus, einfach nur ein Programm mit einem Dateinamen aufzurufen, welches dann die neu angefügten Daten ausgibt.

Ich erwarte mir deutlich mehr als eine einfach "tail.exe". Ideal fände ich eine .NET DLL, welche als PSSnapin einfach nachgeladen werden kann. Vielleicht kann Sie auch noch per COM erreicht werden, damit die VB-Skript-Jünger auch noch etwas davon haben.

Einsatzbereiche

Nun werden Sie sich fragen, warum so was "einfaches" überhaupt entwickelt werden soll. Dazu möchte ich ein paar Beispiele aufführen. Weitere Einsatzbereiche finden Sie auch auf Tail.

Dateien sind eine einfache stabile und flexible Schnittstelle zur Übergabe von Informationen von einem Prozess zu einem anderen Dienst und funktionieren auch Betriebssystemübergreifend und über die meisten (Datei)-Netzwerke.

Wie verfolge ich Änderungen ?

Ich habe mir natürlich schon meine Gedanken gemacht. Es gibt gar mehrere Optionen, möglichst schnell zu erfahren, ob eine Datei sich geändert hat. Vier Verfahren konnte ich ausfindig machen:

$watcher = [system.io.filesystemwatcher]
$watcher.path = ""
watcher.Filter = "*.txt"
$watcher.NotifyFilter = (NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName)

Letztlich mache ich keine Vorgaben, wie die Lösung umzusetzen ist, Aus Kompatibilitätsgründen und der Frage der erforderlichen Berechtigungen könnte ich mir aber vorstellen, dass das fortgesetzte Lesen am Ende der Dateien und die Überwachung nach neuen bzw. gelöschten Dateien mit einem DIR eine sinnvolle Kombination sein könnte.

Umsetzung

Ich will hier nicht allzu viel Vorgreifen. Es kann ja sein dass es viel bessere Algorithmen zur Umsetzung gibt. Eine einfache Umsetzung in Form einer VBScript-Klasse zum fortgesetzten Lesen am Ende einer Datei habe ich auf VBSToolbox unter der URL class.tail.1.0.vbs veröffentlicht, Aber das kommt natürlich überhaupt nicht an das eigentliche Ziel heran. Hier ein paar Pseudo-Codes, die ich immer mal wieder skizziert habe.

Funktionsweise (Option1)

Überwachte Verzeichnis mit FileSystemWatcher (?)
Warte auf Modifikation
IF Dateinamenfiltermatch Dann
    Hole LastPos(Datei) auf Speicher
    IF LastPos = null THEN
        LastPos= 0' New File
    ENDIF
    IF Filesize<LastPost THEN
        LastPost=0 'Rollover Achtung eventuell Doppelverarbeitung
    ENDIF
    Öffne Datei
    Seek "lastpost"
    WHILE not eof
        ReadLine
    WEND
End

Knifflig sind hier ein bisschen die Problematik, dass Textdateien sehr einfach mit einem Textreader gelesen werden könnten, aber dieser keine absolute Positionierung versteht. Also muss die Datei mit einem Binaryreader gelesen werden, aus dem man dann die Zeilen heraus holt aber damit umgehen muss, das die letzte Zeile vielleicht noch nicht komplett geschrieben wurde.

Natürlich könnte man auf eine aktive Reaktion auch verzichten und einfach "pollen". das ist meist sogar einfacher, weil dann das Skript einmaliug gestartet sit, die Änderungen an die Pipeline zur Verarbeitung meldet und sich dann wieder beendet.

Dazu könnte das Skript die selektierten Dateien anhand des Namens und "CreationDate" eindeutig bestimmen und die letzte Position speichern. Bei jedem Aufruf müsste nur geprüft werden, ob die Dateien nun schon länger sind und die Daten liefern. Zudem müssen neue Dateien mit der Länge "0" aufgenommen werden und nicht mehr vorhandene Dateien bereinigt werden.

Schnittstelle: Powershell Commandlet oder allgemeine COM und .NET-Klasse

Wer sich hier heranwagen will, sollte sich etwas mit C# auskennen und überlegen, welche Parameter an die Powershell übergeben werden müssen. Es muss auch nicht unbedingt ein Powershell-Commandlet sein. Es kann auch durchaus eine generische .NET Klasse sein, die man ebenso einfach per Powershell instanzieren kann, z.B.:

$tail = New-Object msxfaq.tail
$tail.filefilter = c:\test\*.log  ' definiere überwachtes Verzeichnis\Datei(en)
$tail.statusstore = filename  ' Datei, zur Speicherung des Status
$tail.setfilter ( array of regex)  ' Filter für relevante Zeilen hinterlegen
$tail.savestatus ' Aktuellen Status sichern, d.h. nach erfolgter Verarbeitung
line = $tail.getline   ' Hole nächste Zeile und Quelle ab.

Wäre die .NET Klasse auch als COM-Objekt zu erreichen, könnte man sie auch aus VBScript einfach verwenden:

#### Beispiel für VBScript (COM-Komponente)
 
' Beispiel:
Set Tail = CreateObject("msxfaq.tail")   ' Instanziere Klasse
Tail.filefilter = c:\test\*.log  ' definiere überwachtes Verzeichnis\Datei(en)
Tail.statusstore = filename  ' Datei, zur Speicherung des Status
Tail.setfilter ( array of regex)  ' Filter für relevante Zeilen hinterlegen
Tail.savestatus ' Aktuellen Status sichern, d.h. nach erfolgter Verarbeitung
line = tail.getline (timeout)   ' Hole nächste Zeile und Quelle ab.

Die verschiedenen Methoden sind natürlich nur ein Vorschlag. 

Sonstiger Einsatz

Natürlich ist der Einsatz eines "Tail" auf Dateien nur ein erster Ansatz. Es gibt viele anderen "Logs," die man so einfach auswerten kann. Es gibt Tools, die z.B. Eventlogs oder AD-Änderungen in Textdateien schreiben können und damit anderen Diensten zugänglich machen. Dies kostet meist weniger Ressourcen, als bei jedem Aufruf z.B. im Eventlog an die letzte Position zu springen und weiter zu verarbeiten. Hier kann Get-Tail also der Schlüssel für eine weitere effektive Verarbeitung sein.

Weitere Links

Keywords:Tail Ideen