PowerShell Logging Made Easy (thinking RAII)

Just a quick something I want to share :-)!
Make your life easier using a scope-based action approach when writing PowerShell.
The idea is simple: group tasks together in blocks that are passed into a function which logs start and end time of that block, as well as doing some logging and error reporting if something goes wrong during exection.

Let’s elaborate:

$start = Get-Date
Do-Something
Do-SomethingElse
$end = Get-Date
$duration = $end-$start
Write-FancyLog $duration "Done something"

This code doesn’t look nice. If you need to measure execution time of different blocks, do logging based on results, catch and evaluate errors, it would be much cleaner to do something like this:

FancyLog -Description "Do Something" {
  Do-Something
  Do-SomethingElse
}

Yes, that looks much better and is easier to grasp.

Without further detours, here’s a basic ‘logging monitor’ that nicely integrates into any PowerShell environment 🙂

function Monitor {
param(
  [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
  [scriptblock]$scriptblk,
  [Parameter(Mandatory = $True)]
  [string]$Description,
  [Parameter(Mandatory = $False)]
  [scriptblock]$ResultReceiver,
  [Parameter(Mandatory = $False)]
  [switch]$CanFail
)
  $_start = Get-Date
  $_ex = $null
  $_res = 0
  try {
    & $scriptblk
    $res = $lastexitcode
  }
  catch {
    $_ex = $_
    $res = $lastexitcode
  }
  finally {
    $_resobj = @{
      Exception   = $_ex
      Result      = $_res
      Description = $Description
      Duration    = ($(Get-Date) - $_start)
		}
		if ($ResultReceiver) {
			$ResultReceiver.Invoke($_resobj) 
		}
    if (-Not $CanFail -And $_ex) {
      throw $_ex
    }
  }
}

Sample usage with dummy-logging:

function PseudoLog {
param(
  [Parameter(Mandatory = $True)]
  [PSCustomObject]$logobj
)
	Write-Host "> $($logobj.Description)"
	Write-Host $($logobj | ConvertTo-Json)
}

The following code continnues after reporting and errror:

Monitor -Description 'This block errs but continues!' -ResultReceiver $function:PseudoLog -CanFail {
	.\filesdoesnotexist
}

The following code breaks on the exception that is thrown, logs correctly and then rethrows the exception:

Monitor -Description 'This block will probably fail!' -ResultReceiver $function:PseudoLog {
	echo 'hello'
	throw 'foobar'
	echo 'cruel world'
}

Hope you enjoy this snippet, happy hacking!

Leave a Reply

Your email address will not be published. Required fields are marked *