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:


When calling a function from my script, all streams are piped to a logging function.
This function separates the debug, verbose, warning and error objects from the resulting object.
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 *&gt;&amp;1 | Split-Streams

Write-Host $Test2 -ForegroundColor White


So now my question:


Is there something wrong with my solution?
Have I missed any problems?
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 *&gt;&amp;1 | Split-Streams | Split-Streams -revert

$DebugPreference = $DebugPreferenceSave     # restore $DebugPreference


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

Write-Messages -Verbose *&gt;&amp;1 | Split-Streams | Split-Streams -revert


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

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