---
title: "PowerShell Logging Best Practices for IT Pros"
description: "Establish clear PowerShell logging guidelines with informative and actionable logs for better understanding and troubleshooting."
canonical: "https://adamtheautomator.com/powershell-logging/"
---

# PowerShell Logging Best Practices for IT Pros

> Establish clear PowerShell logging guidelines with informative and actionable logs for better understanding and troubleshooting.

Source: https://adamtheautomator.com/powershell-logging/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![PowerShell Logging Best Practices for IT Pros](https://adamtheautomator.com/wp-content/uploads/2019/10/list-2389219_1280.png)

# PowerShell Logging Best Practices for IT Pros

[![](https://secure.gravatar.com/avatar/4147968aa2332aa682bcebf295e4e9d0eb2672dee3d9ae0523a00ac51e7d6017?s=192&d=mm&r=g)Bill Kindle](https://adamtheautomator.com/author/bill/)8 October 201913 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [Creating Better Log Messages](#creating-better-log-messages)
*   [Make Events Actionable](#make-events-actionable)
*   [Keep Logs Simple and Human-Readable](#keep-logs-simple-and-human-readable)
*   [Logging Options in Windows](#logging-options-in-windows)
*   [Prerequisites](#prerequisites)
*   [Writing Events to the Windows Event Log](#writing-events-to-the-windows-event-log)
*   [Creating an Event Log Source](#creating-an-event-log-source)
*   [Creating Event Log Entries](#creating-event-log-entries)
*   [Event Source Best Practice](#event-source-best-practice)
*   [Leveraging PowerShell Transcripts](#leveraging-powershell-transcripts)
*   [Logging with the PSFramework PowerShell Module](#logging-with-the-psframework-powershell-module)
*   [Important PSFramework Concepts](#important-psframework-concepts)
*   [Providers](#providers)
*   [Message Streams](#message-streams)
*   [Install PSFramework](#install-psframework)
*   [Setting up a Provider](#setting-up-a-provider)
*   [Logging with the filesystem Provider](#logging-with-the-filesystem-provider)
*   [Viewing the filesystem Log](#viewing-the-filesystem-log)
*   [Logging with the logfile Provider](#logging-with-the-logfile-provider)
*   [Bonus: Querying Logs by Runspace](#bonus-querying-logs-by-runspace)
*   [Using PSFramework in the Real-World: A Note from the Author](#using-psframework-in-the-real-world-a-note-from-the-author)
*   [Summary](#summary)
*   [Further Reading](#further-reading)

Being able to track what [PowerShell](https://adamtheautomator.com/tag/powershell/) scripts are doing is important, especially in a production environment. Recording activity is key to troubleshooting, auditing, and investigating potential security issues. Using PowerShell and a community module known as PSFramework allows you to take your logging to a whole new level which you’ll learn about in this article!

If you find yourself using the `Write-Host` cmdlet to display error information or manually registering Windows event sources, this article is for you.

Stay with us if you want a simple, scalable PowerShell logging solution that can write to a text file or integrate into a log management solution.

## Creating Better Log Messages

Before deciding on how best to generate logs with PowerShell, you need to first understand how to come up with better log messages, to begin with.

There are many reasons to generate logs. One of the most common is the infamous error message. Error messages can be helpful or harmful depending on how you look at things.

You’ve undoubtedly been victim to some bad error messages in your day. If you’ve ever seen the movie Office Space, _PC LOAD LETTER_ is a prime example.

If your error messages cause someone to think, “WTF does that mean?”, you should consider rewriting your message! Logs should be clear and concise.

### Make Events Actionable

A log message indicating a cryptic _PC LOAD LETTER_ error message by itself doesn’t make a lot of sense. No one is going to know what that message means including you a few years from now! You should always generate log messages for error conditions that are actionable.

Using the _PC LOAD LETTER_ message for example, what would you think if it instead said, “_PC LOAD LETTER: Check the specs on the rotary girder. If within spec, contact vendor support for further guidance_“? You’d probably keep your cool a bit more and actually have a jumpstart on fixing the problem.

### Keep Logs Simple and Human-Readable

Log information must be human-readable. Include enough information to make the event actionable and not left to guesswork. You can be precise, but make sure you can have someone else understand, even if on a basic level, what the event means.

Logging is only as complicated as you want to make it. Keeping logs simple makes troubleshooting easier for all involved.

## Logging Options in Windows

When you hear the word ‘log’ in IT, what comes to mind? Chances are you immediately begin to think about log _files_. Although log files are, indeed, an option and one you’ll learn about in this article, they aren’t the only option.

Another alternative is the Windows Event Log. The Windows Event Log and it’s seemingly endless number of log sources is ubiquitous in Windows. The event log allows you to write logs the varying degrees of severity, sources, categories, messages, and more.

The Windows Event Log differs from a log file because it has structure. A log file, on its own, is a simple text file. It’s up to the developer to come up with a schema for the log messages. The Windows Event log already has the schema taken care of.

Related:[Using PowerShell to Read Text Files and Replace Text](https://adamtheautomator.com/powershell-read-text-file/)

Throughout this article, we’ll cover using PowerShell to work with the event log. We’ll also cover logging with log files using the PSFramework [PowerShell module](https://adamtheautomator.com/powershell-modules/ "PowerShell module").

## Prerequisites

The remainder of this article is going to be a walkthrough. If you’ll be following along, you’ll need to ensure we’re both on the same page. To work through each topic in this article, you’ll need:

*   Windows PowerShell 5.1
*   intermediate knowledge of PowerShell
*   to understand basic [Windows event logging terminology](https://docs.microsoft.com/en-us/windows/win32/eventlog/event-logging)

## Writing Events to the Windows Event Log

One way to capture logging information with PowerShell is to use the Windows Event Log. As mentioned earlier, the Windows Event Log already provides a schema to work with. It also includes GUI tools like the Windows Event Viewer and PowerShell cmdlets to create and parse event log messages.

The main PowerShell cmdlet you’ll use to write log events to the Windows Event Log is the `Write-EventLog` cmdlet. The `Write-EventLog` cmdlet is how you can generate new log events in existing event logs. If you’re planning on using existing event sources like the _Application_, _Security_, _System_ event logs, the `Write-EventLog` cmdlet is all you need.

> _The Windows Event Log has a concept known as sources. In a nutshell, a source is simply a way to put event log messages into buckets. Built-in sources are used to separate operating system-specific events from software events, for example._

Using existing sources to write events works just fine if you’d like your log message to show up in a default source. But you can also create your own sources which will help differentiate your custom-generated event log messages from all the others.

### Creating an Event Log Source

You can create a custom event log source by using the `New-EventLog` cmdlet. This cmdlet will modify the registry to make your event log a first-class citizen in Windows.

From an administrative PowerShell console, register a new event log source providing a name and source as shown below.

```powershell
PS51> New-EventLog -LogName 'Application' -Source 'ATA_Script’'
```

If you don’t have appropriate permissions or are running the command under a non-elevated PowerShell console session, you will see an error like below.

![Creating a Windows event log source with PowerShell](https://adamtheautomator.com/wp-content/uploads/2020/06/1-39-1024x122.png)

Creating a Windows event log source with PowerShell

### Creating Event Log Entries

Once you’ve created a source for your script/application, you can then get down to business and begin generating event log messages using the `Write-EventLog` cmdlet.

Below you can see an example of writing an event log message to the Application event log using the event source just created (_ATA\_Script_). This message is an _Informational_ event with an ID of 1.

```powershell
PS51> Write-EventLog -LogName 'Application' -Source 'ATA_Script' -EntryType 'Information' -EventID 1 -Message 'Everything is broken. Everything is cool when you’re part of a team!'
```

Once the event is created, you can then find this event in the Windows Event Viewer or using PowerShell using the `Get-WinEvent` or the [`Get-EventLog`](https://adamtheautomator.com/get-eventlog/) cmdlet. The below example is using the `Get-EventLog` cmdlet to find all Informational events using the event source just created. If more than one event is returned, it returns the newest one.

Related:[Get-EventLog: Querying Windows Event Logs with PowerShell](https://adamtheautomator.com/get-eventlog/)

```powershell
PS51> Get-EventLog -LogName 'Application' -EntryType 'Information' -Source 'ATA_Script’' -Newest 1
```

![Querying an event log with PowerShell](https://adamtheautomator.com/wp-content/uploads/2020/06/Event-Source-Best-Practice-1024x116.png)

Querying an event log with PowerShell

### Event Source Best Practice

Before you leave this section, it’s important you know about a gotcha here. In the above example, PowerShell is using the arbitrary registered source (_ATA\_Script_) created earlier. This source is required to write an entry to the Windows Application log.

Using a custom sources like this works well for simple scenarios like:

*   Event logging for a custom Windows service.
*   Recording results from a simple [PowerShell function](https://adamtheautomator.com/powershell-functions/ "PowerShell function").
*   Providing a trigger event for another process.

As a best practice, register a new event source for each script you want to generate event log entries as shown in the first step. When you do it this way, it’s easier to keep track of what is generating the event log entries. Imagine you have five scripts, each doing a very specific task. If all of them were using the same event source, how could you determine which script had failure? Registering a new event source helps you organize your logging sources.

## Leveraging PowerShell Transcripts

Although the method you’re about to learn works, it’s not the best way. You’ll see why in a bit.

Although the [`Write-Host`](https://adamtheautomator.com/powershell-write-host/ "Write-Host") cmdlet is useful when you need some visual feedback when a script runs, it’s not great at logging. Why?

*   The output is specific to a single PowerShell session. When you close the session, the output is gone.
*   The cmdlet doesn’t have much customization beyond foreground and background colors of text.

Below you can see an example of what `Write-Host` cmdlet looks like in use.

![Write-Host to send output](https://adamtheautomator.com/wp-content/uploads/2020/06/Although-by-default-this-output-is-only-displayed-to-the-PowerShell-console-1024x114.png)

Write-Host to send output

Although by default, this output is only displayed to the PowerShell console and not recorded anywhere else, it can be using [PowerShell transcripts](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.host/start-transcript?view=powershell-7.2&viewFallbackFrom=powershell-6).

Using the `Start-Transcript` cmdlet, you can create transcripts (text files) that capture all output sent to a PowerShell console. The `Start-Transcript` cmdlet is a great way to record PowerShell session activity.

While the `Start-Transcript` cmdlet starts recording console activity to a new transcript, the `Stop-Transcript` cmdlets stops it. You can see an example below.

![Starting and stopping a PowerShell transcript](https://adamtheautomator.com/wp-content/uploads/2020/06/Although-using-the-Write-Host-cmdlet-and-PowerShell-transcripts-technically-works-its-1024x162.png)

Starting and stopping a PowerShell transcript

Although using the `Write-Host` cmdlet and PowerShell transcripts technically works, it’s not the greatest solution. Notice what the transcript contains in the example below. The log is difficult to read and is near impossible to parse!

![PowerShell transcript](https://adamtheautomator.com/wp-content/uploads/2020/06/ogging-with-the-PSFramework-PowerShell-Module-1024x576.png)

PowerShell transcript

## Logging with the PSFramework PowerShell Module

As promised, it’s time to get into logging to text files! Although, you _could_ roll your own log file solution using a combination of the `Add-Content`, [`Set-Content`](https://adamtheautomator.com/powershell-write-to-file/ "Set-Content") and other PowerShell cmdlets, there’s no need. You can use a free PowerShell module called PSFramework!

PSFramework is a PowerShell module that allows you to log activity in a variety of different scenarios.

### Important PSFramework Concepts

#### Providers

A key concept of the PSFramework module is _providers_. Providers are pointers to various destinations you’d like logs to be stored in. A provider could be something like Kiwi Log Server, Elasticsearch, loggly, Graylog or something as basic as a file (usually text).

Although extensible, when you first download the PSFramework module, you immediately get three providers to use:

*   **Filesystem** – This provider writes all message streams you create to the currently-logged-in user’s _%APPDATA%_ folder with automatic seven-day retention and rotation. Filesystem is the default provider.
*   **Gelf** – This provider allows PSFramework to send log message streams to an open source log manager called [Graylog](https://www.graylog.org/products/open-source). There is a little more setup involved when using this option.
*   **Logfile –** This provider allows you to specify a file to write all messages to. Because a log file could be of various types, the default output is CSV-formatted. This means you can use common log file extensions like _.txt_, _.log_, or _.csv_. The choice is yours.

#### Message Streams

Message streams contain output from commands. These are used to classify information being recorded in the logs. Common streams are Verbose, Debug, Information, Error and Warning. You can read more about available streams using `Help about_psf_message` .

Let’s get down to actually use the PSFramework module now and see what it can do!

### Install PSFramework

The first task is to download and install this module. To do so, download it from the PowerShell Gallery by running `Install-Module -Name PSFramework -Scope CurrentUser`. This will place the module in my user profile rather than the system path. If you’d like to make the module available to all users on your system, don’t use the `Scope` parameter.

Once installed, you will now have access to all the cmdlets that are required for the code examples demonstrated in the rest of this article.

### Setting up a Provider

There are three registered and installed providers available to choose from with the default PSFramework configuration – _filesystem_, _logfile_, and _greylog_. If you want to use another provider, you will first register and then install that provider within PSFramework. Before you can start writing to logs, you must enable one of these providers.

The command to enable a provider is `Set-PSFLoggingProvider`. This cmdlet has two required parameters – `Name` and`Enable`. You’ll use each parameter to enable the provider or providers you wish to work with.

If you plan on using your own provider, you will need to run the `Register-PSFLoggingProvider` and the `Install-PSFLoggingProvider` cmdlets. These cmdlets and scenario are outside the intended scope of this article. If you wish to explore this option further, consult the built-in help provided with the module. There are some examples that could lead you to expanding on existing capabilities and provide you with a deeper understanding of how logging works.

For this demonstration, you’ll be using the _logfile_ provider. Since this provider is off by default, you must first enable the provider so that message streams can be sent to it. Using the `Set-PSFLoggingProvider` command, you can do that no sweat:

```powershell
Set-PSFLoggingProvider -Name 'logfile' -Enable $true
```

If you want to stick with the _filesystem_ provider, no action would be required on your part as it’s already enabled by default.

To see all of the currently-registered providers, run the `Get-PSFLoggingProvider` command. This command should return output similar to the screenshot below.

![Finding PSFramework logging providers](https://adamtheautomator.com/wp-content/uploads/2020/06/To-see-all-of-the-currently-registered-providers-run-the-Get-PSFLoggingProvider-command.-This-command-should-return-output-similar-to-the-screenshot-below..png)

Finding PSFramework logging providers

Now that you’ve enabled a provider, you can begin sending message streams to that provider you wish to use for your logging solution.

### Logging with the filesystem Provider

The filesystem provider provides a quick way to start logging without having to configure storage locations for log files. It’s perfect for short-term logging needs, but not one where you need long-term diagnostic logging.

In this demonstration of the filesystem provider, you’re going to learn how to use the `Write-PSFMessage` cmdlet. Imagine you’ve got an existing PowerShell script that starts a service on a remote host. Sometimes the service doesn’t start for some reason and the errors generated are not being captured. You can see the example below.

```powershell
Invoke-Command -ComputerName 'Lab01.domain.local' -ScriptBlock { Start-Service -Name 'BITS' }
```

You’d like to know the `Start-Service` command is able to successfully start the service and when it fails. To do so, you force `Start-Service` to return a [terminating error](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/cmdlet-error-reporting?view=powershell-7.1) and then add [try/catch block](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.2)s to catch any errors that happen.

```powershell
try {
    Invoke-Command -ComputerName 'Lab01.domain.local' -ScriptBlock { Start-Service -Name 'BITS' } -ErrorAction Stop
} catch {

}
```

You now need to log both when `Start-Service` is successful and when it fails. You know that if `Invoke-Command` fails, PowerShell will run code inside of the `catch` block and not any code after `Invoke-Command` runs.

Contrast that behavior with a success where code after `Invoke-Command` will run. You decide to run the`Write-PSFMessage` command in the appropriate spots.

As you can see below, if the code successfully executes, `Write-PSFMessage` will record a helpful message with a level of `Important` and tagged as `Success`.

```powershell
try {
    Invoke-Command -ComputerName 'Lab01.domain.local' -ScriptBlock { Start-Service -Name 'BITS' } -ErrorAction Stop

    Write-PSFMessage -Level Important -Message 'Successfully started BITS.' -Tag 'Success'
} catch {
    Write-PSFMessage -Level Warning -Message 'Error starting BITS.' -Tag 'Failure' -ErrorRecord $_
}
```

> _Above you saw a use of the `Tags` feature. Tags are optional, but it does make it easier when needing to sort and search a log. Tags are free text allowing you to define your own. Some common tags are SUCCESS, FAILURE, ERROR and INFO._

Now if the command fails, you will see a warning written to the PowerShell console as shown below.

![PSFramework filesystem log provider](https://adamtheautomator.com/wp-content/uploads/2020/06/Now-if-the-command-fails-you-will-see-a-warning-written-to-the-PowerShell-console-as-shown-below.-1024x283.png)

PSFramework filesystem log provider

#### Viewing the filesystem Log

Once you’ve written a few message streams with the filesystem provider, you can use the `Get-PSFMessage` cmdlet to query the log.

If the code in the example above generated log messages at the _Warning_ level, you could read all of those messages by using a value of `Warning` for the `Level` parameter. You can see an example of how to do this along with the output type you will receive.

```powershell
PS51> Get-PSFMessage -Level Warning
```

![Querying logging messages](https://adamtheautomator.com/wp-content/uploads/2020/06/Logging-with-the-logfile-Provider-1024x79.png)

Querying logging messages

### Logging with the logfile Provider

Now that you’ve seen how the filesystem provider works, let’s check out the logfile provider. The logfile providers allows you to write message streams to plain-text files, by default, in a CSV format.

To use the logfile provider, you’ll first have to tell PSFramework to not use the default provider (filesystem) and to set a file to record messages to. To do so, use the `Set-PSFLoggingProvider` cmdlet. You can see in the below example how to do this.

*   name of the provider (logfile) via the `Name` parameter
*   setting the `Enabled` property to True
*   setting the destination file as the value for `FilePath`

```powershell
PS51> Set-PSFLoggingProvider -Name logfile -Enabled $true -FilePath 'C:\My_Events.log'
```

_Note: Be sure to set the provider at the beginning of your script!_

When a message is received via running the `Write-PSFMessage` command as before, PSFramework will record the message. The message will include a date format of _mm:dd:yyyy,_ script name (source), [message level](http://allthingspowershell.blogspot.com/2017/07/the-psframework-mastering-levels-of.html), a verbose message, tag (optional), and an error record.

Below is an example of what a single log message will look like in Windows notepad in an unstructured format.

![Querying log message](https://adamtheautomator.com/wp-content/uploads/2020/06/Below-is-an-example-of-what-a-single-log-message-will-look-like-in-Windows-notepad-in-an-unstructured-format.-1024x165.png)

Querying log message

To take advantage of the CSV format, open the log file up in Microsoft Excel and the output will look much better as shown below.

![PowerShell logs in Excel](https://adamtheautomator.com/wp-content/uploads/2020/06/up-in-Microsoft-Excel-and-the-output-will-look-much-better-as-shown-below.-1024x288.png)

PowerShell logs in Excel

My choice to use the logfile provider instead of the filesystem provider was due to a need to keep a long running history and import information into Excel. The format of the output makes it a breeze to generate charts and filter data for reporting.

### Bonus: Querying Logs by Runspace

An advanced feature of the PSFramework module is working with runspaces. A PowerShell runspace is a commonway you can run processes in parallel by spinning up threads.

Since you may have multiple streams of logging running at once in different runspaces, PSFramework allows you to look at multiple logs in real time. These logs can come from many sources through the use of runspaces. This feature is extremely useful as you can leave a single PowerShell session open while running multiple commands or scripts in the background.

If you know the runspace ID, you can specify it using the `Runspace` parameter on the `Get-PSFMessage` command as shown below.

```powershell
Get-PSFMessage -Runspace a2346978-d324-4886-89b8-bd14769be78f
```

Runspace can be a complicated topic to grasp. If you would like to learn more, check out this [Microsoft DevBlog Series](https://devblogs.microsoft.com/scripting/beginning-use-of-powershell-runspaces-part-1/) about using runspaces.

### Using PSFramework in the Real-World: A Note from the Author

I’ve been using the PSFramework module for months now and it has supercharged my maintenance scripts and forced me to write better code.

It’s forced me to write better error handling code and to make a habit of setting the common `ErrorAction` parameter to True on cmdlets to create terminating errors.

I started seeing the benefits instantly. There’s even been an ongoing problem I was able to finally solve using the log file that was generated.

I was running into an error that only occurred when executing a script using scheduled jobs. The script would run fine when executed manually. The issue turned out to be a credential and timing issue. Since the error record ID was in the log file, error messages were clear and using tags, I was able to narrow it down in no time.

Having tried other methods to generate log files and this module is by far the easiest to use.

The PSFramework is module works great, is reliable, and gives you the most control over verbose messages.

## Summary

As you’ve learned throughout this article, logging should be a key component of your automation processes. Establishing clear logging guidelines with informative and actionable logs coupled with understanding logging options will go a long way.

If you haven’t used the PSFramework module and haven’t walked through the demos provided here, please do so. This helpful module allows you to many different options in recording logs which will ultimately help troubleshooting, auditing and security analysis.

## Further Reading

*   [**_PSFramework Documentation_**](https://psframework.org/documentation.html)
*   **_[Friedrich Weinmann: Logging in a DevOps World](https://www.youtube.com/watch?v=Uy5Qd9g25Dg)_**
*   **_[How to Use PowerShell Transcripts](https://mcpmag.com/articles/2015/11/18/use-powershell-transcripts.aspx) by Adam Bertram_**

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-logging%2F&text=PowerShell%20Logging%20Best%20Practices%20for%20IT%20Pros)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-logging%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-logging%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
