# Powershell - Simplified Logging

Today I have dealt with logging in PowerShell as well as with the different streams and the pipeline. Unfortunately, none of these solutions really met my needs.

My requirements are:

• I need to output information from PowerShell to a file log.
• The file log has a predefined structure. So it's not enough to just redirect all streams to the file.
• I want to use mainly standard PowerShell functions such as Write-Error, Write-Warning, Write-Verbose.
• The overhead in the code through logging should be minimal.

I have developed the following idea now:

1. When calling a function from my script, all streams are piped to a logging function.
2. This function separates the debug, verbose, warning and error objects from the resulting object.
3. The resulting object is released back into the pipeline.

Here is my solution:

function Split-Streams {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
$InputStream ) process{ switch($InputStream.GetType())
{
'System.Management.Automation.DebugRecord' {
# Do whatever you want, like formatting an writing to a file.
Write-Host $InputStream -ForegroundColor Gray } 'System.Management.Automation.ErrorRecord' { Write-Host$InputStream -ForegroundColor Red
Write-Host ('Error function: {0}' -f $InputStream[0].InvocationInfo.MyCommand.Name) -ForegroundColor Red } 'System.Management.Automation.VerboseRecord' { Write-Host$InputStream -ForegroundColor Cyan }
'System.Management.Automation.WarningRecord' { Write-Host $InputStream -ForegroundColor Yellow } default { return$InputStream }
}
}
}

function Write-Messages
{
[CmdletBinding()]
param()
Write-Debug "Debug message"
Write-Output "Output message"
Write-Verbose "Verbose message"
Write-Warning "Warning message"
Write-Error "Error message"
}

$Test2 = Write-Messages -Verbose -Debug *>&1 | Split-Streams Write-Host$Test2 -ForegroundColor White


So now my question:

• Is there something wrong with my solution?
• Have I missed any problems?
• Well, nothing will ever end up in a file - Write-Host writes directly to the host screen buffer – Mathias R. Jessen Jul 18 '19 at 13:09
• This is just an example. To show the possibilies of the consturct. you can redirect from there to whatever function you like. ie. "$InputStream | Add-Content -Path$LogFilePath -Encoding ASCII" – kami Jul 18 '19 at 14:16

• Is there something wrong with my solution? I don't think so.
• Have I missed any problems?
• Omitted Information stream, cf How to process Write-Information pipeline output when using SilentlyContinue
• The -Debug parameter overrides the value of the $DebugPreference variable for the current command, setting the value of $DebugPreference to Inquire. Prompts to continue for every Write-Debug statement which may be considered inconvenient (or even harmful) in batch processing.

Here's my solution (partially commented script):

[CmdletBinding()]
param(
# [switch]$debug # alternative to [CmdletBinding()] ) function Split-Streams { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipeline)] [ValidateNotNull()] # avoid crashing . property dereference operator$InputStream,
[switch]$revert ) process { if ( -not$revert.IsPresent ) {
# return an object which contains type and value of $InputStream [PSCustomObject] @{ stream =$InputStream.GetType().FullName
value  = $InputStream } } else { # basic input object validity check if ( ($InputStream.psobject.Properties.Name -join ',') -match
"\bstream\b.*\bvalue\b|\bvalue\b.*\bstream\b" ) {
# review split streams and handle them individually
switch($InputStream.stream) { 'System.Management.Automation.DebugRecord' { # Do whatever you want, like formatting an writing to a file. Write-Host ($InputStream.value) -ForegroundColor Gray
}
'System.Management.Automation.ErrorRecord' {
Write-Host ($InputStream.value) -ForegroundColor Red Write-Host ('Error function: {0}' -f ($InputStream.value).InvocationInfo.MyCommand.Name) -ForegroundColor DarkRed
}
'System.Management.Automation.VerboseRecord' {
Write-Host ($InputStream.value) -ForegroundColor Cyan } 'System.Management.Automation.WarningRecord' { Write-Host ($InputStream.value) -ForegroundColor Yellow
}
'System.Management.Automation.InformationRecord' {
Write-Host ($InputStream.value) -ForegroundColor Green } default { Write-Host "output type:$($InputStream.stream)" -ForegroundColor Blue # keep original output stream unchanged$InputStream.value
}
}
}
}
}
}

function Write-Messages
{
[CmdletBinding()]
param()
Write-Debug       "Debug message $DebugPreference" Write-Verbose "Verbose message$VerbosePreference"
Write-Warning     "Warning message $WarningPreference" Write-Error "Error message$ErrorActionPreference"
Write-Information "Information message $InformationPreference" # Write-Output: it is generally not necessary to use the cmdlet. 'Output message' # I'm checking a more complex object than plain string Write-Output$Host
}

$DebugPreferenceSave =$DebugPreference     # backup $DebugPreference If ($PSBoundParameters['Debug']) {
# The Debug parameter overrides the value of the $DebugPreference # variable for the current command, setting the value # of$DebugPreference to Inquire.
# The following setting suppresses asking whether you want to continue
# even if examined Write-Messages is a third-party black box.
$DebugPreference = 'Continue' } Write-Messages -Verbose *>&1 | Split-Streams | Split-Streams -revert$DebugPreference = $DebugPreferenceSave # restore$DebugPreference


Alternatively to the (sample) usage in the one pipeline:

Write-Messages -Verbose *>&1 | Split-Streams | Split-Streams -revert


(cf above code) you can call it as follows:

$test3 = Write-Messages -Verbose *>&1 | Split-Streams # preprocess the$test3 variable here (optional)
\$test3 | Split-Streams -revert