PSWriteMessage.psm1
|
function Write-Message { <# .SYNOPSIS Writes a string to the pipeline in a specifically formatted manner. .DESCRIPTION Writes a string to the pipeline in a specifically formatted manner. 'Write-Message' includes functionality for writing PowerShell's default messages, including warnings, errors, verbose, and debug messages, while also including generic "info" messages and "success" messages when needed. Additionally, 'Write-Message' includes a formatted date parameter that prefixes every call for easy timestamping. This function can be utilized in place of the various builtin 'Write-*' cmdlets. .PARAMETER Message Any message to be sent, must be enclosed in quotes. .PARAMETER Type The type of message to be sent. Valid options are Debug, Verbose, Info, Success, Warning, and Error. Debug and Verbose only produce output when $DebugPreference / $VerbosePreference is 'Continue' -- pass the common -Debug or -Verbose switch to enable them for a single call. .PARAMETER Clean Removes all ANSI formatting from the message before output. Also enforced automatically on PowerShell versions below 7, and on any 7.x release that predates $PSStyle (7.0-7.1). .PARAMETER NoPrefix Omits the leading type label (e.g. '[INFO]', '[ERROR]') from the message. .PARAMETER OutFile Tee the message to a specified file. -OutFile enforces -Clean on the file only to prevent ANSI definitions from interfering with readability. .NOTES The "Warning" and "Error" types do not write to any builtin variable and do not terminate or throw. Error handling must be done via other methods, 'Write-Message' is purely an informational utility. .EXAMPLE Write-Message "Hello world!" [Tue May 01 12:00:00 2023] [INFO] Hello world! Writes a general message. With no other parameters, the output is assumed to be "Info". .EXAMPLE Write-Message "An error occurred." -Type Error [Tue May 01 12:00:00 2023] [ERROR] An error occurred. Writes an error message to output. .EXAMPLE Write-Message "Hello world!" -NoPrefix [Tue May 01 12:00:00 2023] Hello world! Writes a message without its type label. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory, Position = 0)] [string]$Message, [ValidateSet('Debug', 'Verbose', 'Info', 'Success', 'Warning', 'Error')] [string]$Type = 'Info', [switch]$Clean, [switch]$NoPrefix, [string]$OutFile ) begin { # ┌───I.────────────────────────────────────────┐ # │ Enforce $Clean when using older PowerShell. │ # └─────────────────────────────────────────────┘ if ($PSVersionTable.PSVersion.Major -lt 7) { $Clean = $True } # ┌───II.───────────────────────────┐ # │ Stage parameters and variables. │ # └─────────────────────────────────┘ $Date = $(Get-Date -UFormat "[%a %b %d %T %Y] ") # $PSStyle (PowerShell 7.2+) supplies ANSI styling. Degrade to # no color -- same as -Clean -- on PowerShell versions where # it isn't defined, rather than erroring on Foreground.* access. $UseColor = (-not($Clean)) -and $null -ne $PSStyle if ($UseColor) { $ColorDefault = $PSStyle.Reset } # Per-Type color/bold/prefix lookup, keyed by -Type. Color and # Bold are $null when color is disabled; Prefix is $null when # -NoPrefix is set, so they interpolate as empty strings below. $TypeInfo = @{ Debug = @{ Color = $(if ($UseColor) { $PSStyle.Foreground.FromRgb(0x585858) }) Bold = $(if ($UseColor) { $PSStyle.Bold + $PSStyle.Foreground.FromRgb(0x585858) }) Prefix = $(if (-not($NoPrefix)) { '[DEBUG] ' }) } Verbose = @{ Color = $(if ($UseColor) { $PSStyle.Foreground.FromRgb(0x8A8A8A) }) Bold = $(if ($UseColor) { $PSStyle.Bold + $PSStyle.Foreground.FromRgb(0x8A8A8A) }) Prefix = $(if (-not($NoPrefix)) { '[VERBOSE] ' }) } Info = @{ Prefix = $(if (-not($NoPrefix)) { '[INFO] ' }) } Success = @{ Color = $(if ($UseColor) { $PSStyle.Bold + $PSStyle.Foreground.BrightGreen }) Prefix = $(if (-not($NoPrefix)) { '[SUCCESS] ' }) } Warning = @{ Color = $(if ($UseColor) { $PSStyle.Bold + $PSStyle.Foreground.BrightYellow }) Prefix = $(if (-not($NoPrefix)) { '[WARNING] ' }) } Error = @{ Color = $(if ($UseColor) { $PSStyle.Bold + $PSStyle.Foreground.BrightRed }) Prefix = $(if (-not($NoPrefix)) { '[ERROR] ' }) } } } process { # ┌───III.─────────────────────────────────────┐ # │ Format the message for output to the host. │ # └────────────────────────────────────────────┘ switch ($Type) { 'Debug' { if ($DebugPreference -eq 'Continue') { $t = $TypeInfo.Debug $MessageContent = "$($t.Color)$Date$($t.Bold)$($t.Prefix)$($t.Color)$Message$ColorDefault" } } 'Verbose' { if ($VerbosePreference -eq 'Continue') { $t = $TypeInfo.Verbose $MessageContent = "$($t.Color)$Date$($t.Bold)$($t.Prefix)$($t.Color)$Message$ColorDefault" } } 'Info' { $MessageContent = "$Date$($TypeInfo.Info.Prefix)$Message$ColorDefault" } default { $t = $TypeInfo[$Type] $MessageContent = "$Date$($t.Color)$($t.Prefix)$ColorDefault$Message" } } # ┌───IV.────────────────────────────────────────┐ # │ Format the message for output to a log file. │ # └──────────────────────────────────────────────┘ if ($OutFile) { if ($DebugPreference -eq 'Continue') { $t = $TypeInfo.Debug Write-Output "$($t.Color)$Date$($t.Bold)$($t.Prefix)$($t.Color)`$OutFile detected as $OutFile." } try { switch ($Type) { 'Debug' { if ($DebugPreference -eq 'Continue') { Add-Content -Path $OutFile -Value "$Date$($TypeInfo.Debug.Prefix)$Message" } } 'Verbose' { if ($VerbosePreference -eq 'Continue') { Add-Content -Path $OutFile -Value "$Date$($TypeInfo.Verbose.Prefix)$Message" } } default { Add-Content -Path $OutFile -Value "$Date$($TypeInfo[$Type].Prefix)$Message" } } } catch { $t = $TypeInfo.Error Write-Output "$Date$($t.Color)$($t.Prefix)$($ColorDefault)An error occurred while attempting to write to the output file. `n $($_.Exception.Message)" } } } end { if ($null -ne $MessageContent) { $MessageContent } } } Export-ModuleMember -Function Write-Message |