# Thread-safe PowerShell Logging with PSFramework

Page content

In my previous post, I mentioned that I wasn’t checking the status of my RSJobs because I had logging built into my function. I originally tried to log out to plain text files with Out-File, but quickly hit a wall. In a multi-threaded script, attempting to write to the same text file from multiple threads results in collisions and waits at best, and outright failure at worse. Perplexed, I turned to the PowerShellHelp channel in the SQL Community Slack and asked for suggestions for thread-safe logging to text files.

## Here Comes PSFramework

Fred Weinmann (blog | twitter) popped up quickly and suggested his PSFramework PowerShell module. It’s being used in dbatools as well and has a variety of very useful functions, but of primary interest to me for this project are the logging functions. Fred provides a thread-safe logging function which is easy to both set up and use. How easy?

• Setup is one function, called a single time (outside your multi-threaded process)
• Writing to the log is one function (called from anywhere)

That’s…pretty easy. And even better, it’s non-blocking. That is, once you throw a message “over the wall” to the logging function, you don’t have to wait for it to complete before your script moves on. That wait could be a few milliseconds, or a few minutes, depending on what else is going on. But that’s OK because PSFramework is just putting the message into a queue to be written later, while you move on to doing other things.

## Setup and Usage

PSFramework is available from the PSGallery, so installation is just Install-Module PSFramework. To set up PSFramework’s logging, use Set-PSFLoggingProvider. At the time of this writing PSFramework supports three providers:

In my case, I wanted to use logfile - just writing my log messages out to a plain text file. Just provide the function with the full path to the logfile and tell it to enable the provider. filesystem will also produce a file (name determined by the module itself) with slightly less data and no header row.

$LogFile = Join-Path -path "e:\tmp\poshrsjob" -ChildPath "Demo-$(Get-date -f 'yyyyMMddHHmmss').txt";
Set-PSFLoggingProvider -Name logfile -FilePath $LogFile -Enabled$true;

Now we’re ready to start writing to the log.

Writing messages out to your log is really easy - just call Write-PSFMessage. It supports various “levels” for your messages and other parameters.

Write-PSFMessage -Message "$(Get-Date -format 'o') | I'm writing a blog post!"; Inside my try/catch blocks, I used it to log exceptions a bit differently. Write-PSFMessage -Message "$(Get-Date -format 'o') | Exception when doing something important. $($Error[0])" -Level Important;

A full sample script:

You may notice an extra line in the Scriptblock for the downloads as compared to my previous post. That’s because I ran this demo on PowerShell 5.1, where TLS 1.2 isn’t the default like it apparently is on PowerShell Core.

## Output

What’s the log look like? About what you’d expect.

In this case, several of the fields are empty or anonymous because I was logging from outside a module or even a file (it’s all Scriptblocks). This log also demonstrates that my download jobs completed out of order due to the random delays I included.

## Conclusion

I’ve only demonstrated a basic use case here, but hopefully you can see where this is going. There are a number of other great options for managing your logging with this module and I’m sure it’ll expand even more in the future. Check out Get-Help about_psf_message for all the details about how it works and what you can do with it - there’s extensive documentation here. This functionality is only the top of the iceberg that is PSFramework, too.

Whether you’re multi-threading or not, this is a great way to add lightweight, non-blocking logging to your PowerShell scripts.