Einfaches Logging für PowerShell-Skripte

PowerShell 5 Icon

Lange und komplexe PowerShell-Skripte werden schnell unübersichtlich und man verliert den Überblick, wo das Skript gerade steht und was aktuell abgearbeitet wird. Aus diesem Grund solltet ihr in jedem Skript bereits von Anfang an eine einfache Log-Funktionalität einbauen. Dies ist in vielen Fällen schneller und einfacher zu realisieren, als das Logging später aufwändig nachzurüsten. Vor allem bei Skripten, welche kritische Daten schreiben, ist das Logging im Nachhinein oftmals ein Segen. Beispielsweise wenn euer PowerShell-Skript Daten im AD ändert und im Nachgang einige Änderungen rückgängig gemacht werden müssen.

Daher habe ich eine kleine Funktion geschrieben, welche ich nahezu am Anfang jedes PowerShell-Skripts einbinde. Die Ausgabe erfolgt sowohl in der Konsole als auch in einer Log-Datei. Neben dem aktuellen Datum und der Uhrzeit ermöglicht die Log-Funktion zudem die Definition von verschiedenen Log-Leveln.

Das Skript steht auch als Download zur Verfügung.

$path = "C:\Temp"
$date = get-date -format "yyyy-MM-dd-HH-mm"
$file = ("Log_" + $date + ".log")
$logfile = $path + "\" + $file

function Write-Log([string]$logtext, [int]$level=0)
{
	$logdate = get-date -format "yyyy-MM-dd HH:mm:ss"
	if($level -eq 0)
	{
		$logtext = "[INFO] " + $logtext
		$text = "["+$logdate+"] - " + $logtext
		Write-Host $text
	}
	if($level -eq 1)
	{
		$logtext = "[WARNING] " + $logtext
		$text = "["+$logdate+"] - " + $logtext
		Write-Host $text -ForegroundColor Yellow
	}
	if($level -eq 2)
	{
		$logtext = "[ERROR] " + $logtext
		$text = "["+$logdate+"] - " + $logtext
		Write-Host $text -ForegroundColor Red
	}
	$text >> $logfile
}

# log something
Write-Log "this is a simple log test"

# create warning log entry
Write-Log "this is a simple log test" 2

# use more than simple variables in a string
$cmds = get-command
Write-Log "there are $($cmds.count) commands available"

Tobi

Hallo, mein Name ist Tobias und ich habe diesen Blog im April 2009 ins Leben gerufen. Seitdem blogge ich hier über Software, Internet, Windows und andere Themen, die mich interessieren. SSDblog ist mein zweiter Blog, indem es rund um das Thema SSDs geht. Ich würde mich freuen, wenn ihr meinen Feed abonniert oder mir auf Twitter, Facebook, Google+ und Google+ (privat) folgt.

4 Antworten

  1. Jonas sagt:

    Hallo!
    Ich bin auf dein Script gestoßen, was mir auch schon mal sehr geholfen hat.
    Ein Problem habe ich allerdings. Per Invoke-command lasse ich eine Funktion auf einem remote-System laufen. Das an sich klappt alles, nur wenn ich meine Write-Host durch dein Write-Log ersetze meckert er, dass er „Write-Log“ nicht kennt, da es vermutlich in dem Invoke-Command-Kontext nicht bekannt ist.
    Hast du eine Idee, wie sich das umgehen lässt?

    Vielen Dank
    Jonas

    • Tobi sagt:

      So müsste das klappen:

      function Write-Log([string]$logtext, [int]$level=0)
      {
      	$logdate = get-date -format "yyyy-MM-dd HH:mm:ss"
      	if($level -eq 0)
      	{
      		$logtext = "[INFO] " + $logtext
      		$text = "["+$logdate+"] - " + $logtext
      		Write-Host $text
      	}
      	if($level -eq 1)
      	{
      		$logtext = "[WARNING] " + $logtext
      		$text = "["+$logdate+"] - " + $logtext
      		Write-Host $text -ForegroundColor Yellow
      	}
      	if($level -eq 2)
      	{
      		$logtext = "[ERROR] " + $logtext
      		$text = "["+$logdate+"] - " + $logtext
      		Write-Host $text -ForegroundColor Red
      	}
      	$text >> $logfile
      }
      
      Invoke-Command -ComputerName TESTNAME -ScriptBlock ${Function:Write-Log} -argumentlist "Log!"

      Alternativ kannst du die Funktion auch einfach als Datei kopieren und ausführen lassen:

      Invoke-Command –ComputerName server –FilePath .\log.ps1
  2. Jonas sagt:

    Danke dafür!
    Ich fürchte, dass mein Problem etwas komplizierter ist. Bin leider noch kein PowerShell-Profi, daher weiß ich nicht, ob das überhaupt so machbar ist.

    Ich habe eine Funktion (TestServer), welche entweder lokal ausgeführt wird, oder remote per invoke-command, so wie du angegeben hast mit ${funcion:TestServer}.
    In der Funktion TestServer sind deine Write-Logs drin, z.B.:
    Write-Log 9 „Edition: $((Get-ItemProperty „HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p\Setup“).Edition)“ (etwas angepasst, habe 2 LogLevel hinzugefügt und die parameter umgedreht).
    Wenn ich jetzt die Funktion Write-Log IN die Funktion TestServer schiebe (also function in function), klappt es, ich bekomme es allerdings nur in der Ausgabe zu sehen, es wird nicht ins Log geschrieben (vermutlich, weil es irgendwo ins Log auf dem Remote-System geht). Ein Get-Location in dem Skript welches per Invoke-Command ausgeführt wird gibt mir an, dass der aktuelle Pfad auf dem Remote-System C:\users\USERNAME\documents ist. Dort finden sich allerdings keinerlei Logs.

    Ein wie von dir vorgeschlagener Aufruf scheint mir nicht praktikabel, da ich denselben Code für local und remote Server habe (einmal direkt, einmal wie gesagt per invoke-command).
    Aber vielleicht muss ich mir was anderes überlegen und zwei verschiedene funktionen für local und remote bauen (was ich erst so hatte, dann aber um doppelten code zu vermeiden umgebaut habe)

    • Jonas sagt:

      Ich habe glaube ich eine Lösung gefunden:

      > function TestServer {
      >>>>function Write-Log { … }
      > Write-Log 1 „testausgabe“
      >}

      Invoke-Command -ComputerName $sqlserver -ScriptBlock ${Function:TestServer} | Tee-Object -FilePath $logfile -Append

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.