Main | Contact | Blog | Documentation

PowerShell Framework

The project dedicated to empowering your PowerShell scripting.

Writing Messages

Back to: Logging

What is a Message?

Before we start into the details, it may be valuable to define what Messages are all about. With all the different ways in which a piece of PowerShell can communicate and transmit information, it is easy to go wrong with the best intentions.

Fundamentally, in PowerShell it filters down to two scenarios:

This distinction and its consequences has been further documented in this blog post.

What does a Message contain?

In the PSFramework, a message is a rich data object, offering valuable debugging information. In order to maximize the benefit, both the code you write as well as the system provide pieces of information.

What the user should/can provide

What the system adds

How to write messages

There are several commands that allow you to write messages.

Many of these commands are also part of the Flow Control Component

Command Description
Write-PSFMessage The most commonly used command to write messages. It gives you the maximum flexibility in what to write.
Stop-PSFFunction Part of the Flow Control Component, it combines message handling with error handling.
Invoke-PSFProtectedCommand Part of the Flow Control Component, it combines message handling with error handling, should process and retry logic.

Write-PSFMessage

As the default command to use when writing messages, expect to see a lot of this command.

A simple message could look like this:

Write-PSFMessage -Message "Test Message"

Without specifying a level, the message will be written to the Verbose level.

To make the message more visible, we could instead set the Level to Host:

Write-PSFMessage -Level Host -Message "Message visible to the user"

Some internal debugging message could instead be written like this:

Write-PSFMessage -Level Debug -Message "Very well hidden message"

Finally, if we want to really put the user on notice, there is also the Warning level:

Write-PSFMessage -Level Warning -Message "Warning Message"

Improving the ability to analyze logs / messages

The key tools to enable analyzing large log data is adding targets and tags. When doing said analysis, we generally come from one of either of these two direction:

Tags allow you to classify events, setting Targets allows you to track the subject you are processing.

Examples:

# Target
Write-PSFMessage -Message "Installing update $($update.KBNumber)" -Target $ComputerName

# Tag
Write-PSFMessage -Message "Connecting to database" -Tag connect, database

# Both
Write-PSFMessage -Level Warning -Message "Failed to install update $($update.KBNumber)" -Target $ComputerName -Tag update, failed, error -ErrorRecord $_

Dealing with Errors

The simple way to add errors to messages is using the -ErrorRecord parameter, such as like this:

try { $null.ToString() } # this will fail
catch { Write-PSFmessage -Level Warning -Message "Failed operation" -ErrorRecord $_ }

This will attach the full error record to the message:

(Get-PSFMessage | Select-Object -last 1).ErrorRecord | Format-List -Force

Allowing you to analyze the actual error in the In-Memory debug log. It will also add the error message to the message written to screen if not already included, so you need to do nothing to pass it along to the end user. Important though: Adding an error record will not automatically turn the message into a warning. You still need to explicitly set the Level to a user-visible Level if you want the user to see the message.

If you do not want the system to attach the original error message (e.g. when that message is misleading and you added a more user friendly message), you can disable this with the -OverrideExceptionMessage parameter:

try { $null.ToString() } # this will fail
catch { Write-PSFmessage -Level Warning -Message "Failed operation" -ErrorRecord $_ -OverrideExceptionMessage }

Adding Data

The -Data parameter allows you to add additional arbitrary data. In opposite to the rest of the message properties, there is no promised compatibility or implementation for the Data field in respect to Logging Providers. Some Logging Providers may implement it, others may not or only partially.

You can find the supportability info in the stats on each respective Provider’s status sheet.

Data will always be fully accessible in the In-memory debug log.

Example adding data:

Write-PSFMessage -Message 'Restarting computer' -TargetObject $ComputerName -Data @{
    UpdatesInstalled = $updatesInstalled
    UpdatesPending = $pending
    InstallDuration = $duration
}

Stop-PSFFunction

As previously mentioned, Stop-PSFFunction is a major part of the Flow Control Component. Its main purpose is implementing the opt-in exception user-experience.

From a message perspective, it offers less tuning options than Write-PSFMessage, but the ones it does match 1:1.

All messages generated thus …

If the function is called in terminating exception mode, by default, the warning will be suppressed (but written to log), giving the terminating exception precedence. There is a Feature Flag to write the warning anyway. Feature Flags can be set at module level or globally.

If you want your module’s calls to Stop-PSFFunction to always show warnings, run the following line during module import:

Set-PSFFeature -Name PSFramework.Stop-PSFFunction.ShowWarning -Value $true -ModuleName MyModule

To define this setting globally for the entire PowerShell session, omit the module name:

Set-PSFFeature -Name PSFramework.Stop-PSFFunction.ShowWarning -Value $true

The choice to set a feature at global level should not be made by a module, but by the user her-/himself.

Example use of Stop-PSFFunction:

Stop-PSFFunction -Message "Failed to delete $Path" -ErrorRecord $_ -EnableException $EnableException
return

Invoke-PSFProtectedCommand

Invoke-PSFProtectedCommand too is part of the Flow Control Component and can tie into the opt-in exception user-experience. Its main purpose however is to simplify error handling and ShouldProcess support. See its dedicated feature page to learn how it can simplify your implementation of advanced reliability features in PowerShell.

However, from a message perspective, it will automatically write the following messages:

The command supports several of the options Write-PSFmessage supports (e.g. Tags) and requires specifying a target. This provides significant simplification in your flow control, while providing rich log analysis data.

Example use:

Invoke-PSFProtectedCommand -Action Delete -Target $Path -ScriptBlock {
    Remove-Item -Path $Path -Recurse -Force -Confirm:$false -ErrorAction Stop
} -EnableException $EnableException -Continue

Message vs. String

If you inspect the above.mentioned command’s syntax, you will notice that all of them have two parameter sets:

Both result in effectively the same message, the difference is in how that message is put together, how you need to provide its data. The latter option is used to implement multilingual messages! It requires you to prepare one or several strings files that map string keys to their message text in each file’s respective language. In the -String/-ActionString parameter you then specify the key to use.

This would allow you to prepare messages in multiple languages and offer them in the language most convenient to a given user.

The Logging Language is independent of the display language! You can show warnings in Mandarin and still write logs in English.

Back to: Logging