PowerShell – Logfiles sortieren, parsen und mehr

Es gibt viele Parser, die Sie nutzen können, um Logfiles auszuwerten. Allerdings bietet die PowerShell schon von Haus aus alles, was Sie benötigen.

Zählen, sortieren, …

Fangen wir mal mit dem Sortieren und “Zählen” von Logfiles an. Ich möchte exemplarisch wissen, an welchen Tagen wie viele Logfiles erstellt wurden. Im Beispiel befassen wir uns mit den SMTP Send/Receive Logs eines Exchange 2010 HubServers. Das Ganze läßt sich natürlich auf alle Files anwenden.

Wie jedes Objekt, bringt auch jede Datei Properties mit, die sich weiter verarbeiten lassen. Um sich diese ansehen zu können, wählt man eine Datei und gibt sich alle Properties aus. Wir bleiben beim Beispiel der Exchange Logfiles und sehen uns die Properties an:

Get-ChildItem -recurse -Include *recv201508* |select *

PSPath : Microsoft.PowerShell.Core\FileSystem::C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles
\Logs\ProtocolLog\SmtpReceive\RECV20150831-1.LOG
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles
\Logs\ProtocolLog\SmtpReceive
PSChildName : RECV20150831-1.LOG
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
VersionInfo : File: C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\Logs\ProtocolLog\Sm
tpReceive\RECV20150831-1.LOG
InternalName:
OriginalFilename:
FileVersion:
FileDescription:
Product:
ProductVersion:
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language:

BaseName : RECV20150831-1
Mode : -a—
Name : RECV20150831-1.LOG
Length : 9783413
DirectoryName : C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\Logs\ProtocolLog\SmtpReceive
Directory : C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\Logs\ProtocolLog\SmtpReceive
IsReadOnly : False
Exists : True
FullName : C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\Logs\ProtocolLog\SmtpReceive\RECV2015
0831-1.LOG
Extension : .LOG
CreationTime : 31.08.2015 02:00:01
CreationTimeUtc : 31.08.2015 00:00:01
LastAccessTime : 31.08.2015 02:00:01
LastAccessTimeUtc : 31.08.2015 00:00:01
LastWriteTime : 01.09.2015 02:00:02
LastWriteTimeUtc : 01.09.2015 00:00:02
Attributes : Archive

Als eine Fileinfo steht die Creationtime zur Verfügung. Wenn Sie nun, die Logs der letzten 14 Tage sehen möchten, können Sie dies mit der folgenden Abfrage realisieren:

Get-ChildItem |? {$_.creationtime -gt (get-date).adddays(-14)}

Jetzt zur eigentlichen Anforderung, die Logfiles an den einzelnen Tagen zu zählen. Das erreichen Sie, wenn sie folgenden Befehl absetzen. Dieser liest in einer Schleife die Logfiles der letzten 14 Tage ein und addiert sie jeweils für einen Tag.

-13..0 |% {$e=$_ ; ($Items |? {$_.CreationTime -gt (Get-Date).AddDays(-14)} |? {$_.CreationTime.Date -eq (Get-Date).AddDays($e).Date}).count}

Das Ganze funktioniert zwar, ist aber für eine große Anzahl von Logfiles absolut unpraktikabel. Abhängig von der Anzahl der Logfiles, kann es äußerst lange dauern, bis alle eingelesen sind. Der Grund dafür ist, dass die Schleife 14 mal durchläuft und bei jedem Durchlauf alle Files einliest, um dann zu erfassen welche für den entsprechenden Tag addiert werden müssen. Optimieren kann man dies noch dadurch, dass einmalig alle Files in eine Variable eingelesen werden und die Datumsprüfung dann über die Variable läuft. Dazu werden zuerst alle Files der letzten 14 Tage eingelesen:

$Items = Get-ChildItem -recurse |? {$_.CreationTime -gt (Get-Date).AddDays(-14)}

Dann lassen wir die Schleife über die Variable laufen, was schon wesentlich schneller abläuft:

-13..0 |% {$e=$_ ; ($Items |? {$_.CreationTime -gt (Get-Date).AddDays(-14)} |? {$_.CreationTime.Date -eq (Get-Date).AddDays($e).Date}).count}

Optimal ist diese Lösung aber immer noch nicht. Wenn wir bedenken, dass alle Files schon in einer Variablen stehen und alle Files das CreationTime Property mitbringen, kann die Variable einfach nach diesem Property mit Group-Object gruppiert werden.

$Items | % { $_.CreationTime.Date.ToShortDateString} | Group-Object |ft name,count -autosize

Hierbei ist es wichtig, dass Sie das Property $_.CreationTime.Date auswählen, da sonst die Uhrzeit mit berücksichtigt wird. $_.CreationTime.Date liefert zwar auch eine Uhrzeit, aber diese ist immer leer, bzw. 00:00:00 und damit zum Gruppieren gleich.

Parsen

Nun bleiben wir bei den SMTP Logs des Exchange Servers und versuchen eine eMail zu finden.

Wir wissen, dass irgendwann in den letzten 14 Tagen eine Mail an blog@langlitz-it.de gesendet wurde. Also suchen wir in den Logfiles nach diesem Empfänger. Wir haben schon alle Files der letzten 14 Tage in der Variablen $Items eingelesen und können somit diese Variable weiter nutzen.

$Items | Get-Content |? {$_ -like “*RCPT TO:<blog@langlitz-it.de>*”}

Als Ergebnis erhalten wir die entsprechenden Einträge in den Logs.

2015-09-11T13:54:20.602Z,ServerName\ConnectorName,08D27BC0FD7915C3,36,AbsendeIP:25,EmpfängerIP:Port,<,RCPT TO:<blog@langlitz-it.de>,

Genau so gut können Sie nun auch die Anzahl der Mails der letzten 14 Tage suchen:

($Items | Get-Content |? {$_ -like “*RCPT TO:<blog@langlitz-it.de>*”}).count

Wenn Sie nur bestimmte Daten aus einem Logfile wünschen, können Sie die einzelnen Zeilen auch noch splitten. Im Beispiel der SMTP Logs könnten Sie sich z.B. nur den Zeitstempel, die Empfänger IP und die Empfänger Adresse anzeigen lassen:

$Items |Get-Content |? {$_ -like “*RCPT TO:<blog@langlitz-it.de>*”} |% {$_.split(“,”)[0,5,7]}

Mit dem Split trennen Sie die Ergebniszeile, die durch ein “,” separiert ist auf und liefern in einem Array die Felder [0] (Zeitstempel), [5] (Empfänger IP:Port) und [7] (Empfänger Mailadresse) in einer Liste zurück.

2015-09-11T00:01:52.237Z
EmpfängerIP:Port
RCPT TO:<blog@langlitz-it.de>

Bei dieser Vorgehensweise sind prinzipiell keine Grenzen gesetzt.