Write-ErrorStack - Getting All Detailed Information For Errors

Topics: Archive - Toolkit Extensions
Developer
Jul 9, 2014 at 5:33 AM
Call function like this:
Write-Host "Failed to write to the log file `n$(Write-ErrorStack)" -ForegroundColor Red
Function Write-ErrorStack ([System.Management.Automation.ErrorRecord]$ErrorRecord = $Error[0])
{
    If ($ErrorRecord)
    {
        # Capture Error Record
        $LogMessage += "`n" + "Error Record:"
        $ErrorRecordMsg = $ErrorRecord | Format-List -Property * -Force | Out-String
        $LogMessage += "`n" + $ErrorRecordMsg
        
        # Error Invocation Information
        $LogMessage += "`n" + "Error Invocation Information:"
        $ErrorInvocationMsg = $ErrorRecord.InvocationInfo | Format-List -Property * -Force | Out-String
        $LogMessage += "`n" + $ErrorInvocationMsg
        
        # Capture Error Exception
        If ($ErrorRecord.Exception)
        {
            $LogMessage += "`n" + "Error Exception:"
            $ErrorExceptionMsg = $ErrorRecord.Exception | Format-List -Property * -Force | Out-String
            $LogMessage += "`n" + $ErrorExceptionMsg
            
            # Capture Error Inner Exception(s)
            If ($ErrorRecord.Exception.InnerException)
            {
                $LogMessage += "`n" + "Error Inner Exception(s):"
                
                $ErrorException = $ErrorRecord.Exception
                $count = 0
                
                While ($ErrorException.InnerException)
                {
                    $InnerExceptionSeperator = "*" * 40
                    $ErrorException = $ErrorException.InnerException
                    $ErrorInnerExceptionMsg = $ErrorException | Format-List -Property * -Force | Out-String
                    If ($count -gt 0)
                    {
                        $LogMessage += "`n" + $InnerExceptionSeperator
                    }
                    $LogMessage += "`n" + $ErrorInnerExceptionMsg
                    
                    $count++
                }
            }
        }
        
        #Capture Error Script Stack Trace
        If ((Get-Member -InputObject $ErrorRecord -Name 'ScriptStackTrace') -ne $null)
        {
            #PS 3.0 has a stack trace on the ErrorRecord; if we have it, use it
            $LogMessage += "`n" + "Error Script Stack Trace:"
            $ErrorStackTraceMsg = $ErrorRecord.ScriptStackTrace | Format-List -Property * -Force | Out-String
            $LogMessage += "`n" + $ErrorStackTraceMsg
        }
        
        Write-Output $LogMessage
    }
}
Developer
Aug 8, 2014 at 4:48 PM
Made some major updates to the function and also changed the name.
Function Resolve-Error
    {
    <#
    .SYNOPSIS
        Enumerate error record details.
        
    .DESCRIPTION
        Enumerate an error record or a collection of error record properties. By default, the details
        for the last error will be enumerated.
        
    .PARAMETER ErrorRecord
        The error record to resolve. The default error record is the lastest one: Error[0].
        This parameter will also accept an array of error records.
        
    .PARAMETER Property
        The list of properties to display from the error record. Use "*" to display all properties.
        Default list of error properties is: FullyQualifiedErrorId, ScriptStackTrace, PositionMessage, Message, InnerException
        
        Below is a list of all of the possible available properties on the error record:
        
        Error Record:               Error Invocation:           Error Exception:                    Error Inner Exception(s):
        $_                          $_.InvocationInfo           $_.Exception                        $_.Exception.InnerException
        -------------               -----------------           ----------------                    ---------------------------
        writeErrorStream            MyCommand                   ErrorRecord                         Data
        PSMessageDetails            BoundParameters             ItemName                            HelpLink
        Exception                   UnboundArguments            SessionStateCategory                HResult
        TargetObject                ScriptLineNumber            StackTrace                          InnerException
        CategoryInfo                OffsetInLine                WasThrownFromThrowStatement         Message
        FullyQualifiedErrorId       HistoryId                   Message                             Source
        ErrorDetails                ScriptName                  Data                                StackTrace
        InvocationInfo              Line                        InnerException                      TargetSite
        ScriptStackTrace            PositionMessage             TargetSite                          
        PipelineIterationInfo       PSScriptRoot                HelpLink                            
                                    PSCommandPath               Source                              
                                    InvocationName              HResult                             
                                    PipelineLength              
                                    PipelinePosition            
                                    ExpectingInput              
                                    CommandOrigin               
                                    DisplayScriptPosition       
        
    .PARAMETER GetErrorRecord
        Get error record details as represented by $_
        Default is to display details. To skip details, specify -GetErrorRecord:$false
        
    .PARAMETER GetErrorInvocation
        Get error record invocation information as represented by $_.InvocationInfo
        Default is to display details. To skip details, specify -GetErrorInvocation:$false
        
    .PARAMETER GetErrorException
        Get error record exception details as represented by $_.Exception
        Default is to display details. To skip details, specify -GetErrorException:$false
        
    .PARAMETER GetErrorInnerException
        Get error record inner exception details as represented by $_.Exception.InnerException.
        Will retrieve all inner exceptions if there are more then one.
        Default is to display details. To skip details, specify -GetErrorInnerException:$false
        
    .EXAMPLE
        Resolve-Error
        
        Get the default error details for the last error
        
    .EXAMPLE
        Resolve-Error -ErrorRecord $Error[0,1]
        
        Get the default error details for the last two errors

    .EXAMPLE
        Resolve-Error -Property *
        
        Get all of the error details for the last error
        
    .EXAMPLE
        Resolve-Error -Property InnerException
        
        Get the "InnerException" for the last error
    .EXAMPLE
        Resolve-Error -GetErrorInvocation:$false
        
        Get the default error details for the last error but exclude the error invocation information
    .LINK
    #>
        [CmdletBinding()]
        Param
        (
            [Parameter(Mandatory=$false,Position=0)]
            [ValidateNotNullorEmpty()]
            [System.Management.Automation.ErrorRecord[]]$ErrorRecord = $Error[0],
            
            [Parameter(Mandatory=$false,Position=1)]
            [ValidateNotNullorEmpty()]
            [string[]]$Property = ('Message','InnerException','FullyQualifiedErrorId','ScriptStackTrace','PositionMessage'),
            
            [Parameter(Mandatory=$false,Position=2)]
            [switch]$GetErrorRecord = $true,
            
            [Parameter(Mandatory=$false,Position=3)]
            [switch]$GetErrorInvocation = $true,
            
            [Parameter(Mandatory=$false,Position=4)]
            [switch]$GetErrorException = $true,
            
            [Parameter(Mandatory=$false,Position=5)]
            [switch]$GetErrorInnerException = $true
        )
        
        Begin
        {
            # Define script block for selecting and filtering the properties on the error object
            $SelectProperty = {
                Param
                (
                    $InputObject,
                    $Property
                )
                [string[]]$ObjectProperty = $InputObject | Get-Member -MemberType *Property | Select-Object -ExpandProperty Name
                ForEach ($Prop in $Property)
                {
                    If ($Prop -eq '*')
                    {
                        [string[]]$PropertySelection = $ObjectProperty
                        Break
                    }
                    ElseIf ($ObjectProperty -contains $Prop)
                    {
                        [string[]]$PropertySelection += $Prop
                    }
                }
                Write-Output $PropertySelection
            }
        }
        Process
        {
            ForEach ($ErrRecord in $ErrorRecord)
            {
                If ($GetErrorRecord)
                {
                    # Capture Error Record
                    $LogErrorRecordMsg = $ErrRecord |
                        Select-Object -Property $(&$SelectProperty -InputObject $ErrRecord -Property $Property)
                }
                
                If ($GetErrorInvocation)
                {
                    If ($ErrRecord.InvocationInfo)
                    {
                        # Error Invocation Information
                        $LogErrorInvocationMsg = $ErrRecord.InvocationInfo |
                            Select-Object -Property $(&$SelectProperty -InputObject $ErrRecord.InvocationInfo -Property $Property)
                    }
                }
                
                If ($GetErrorException)
                {
                    If ($ErrRecord.Exception)
                    {
                        # Capture Error Exception
                        $LogErrorExceptionMsg = $ErrRecord.Exception |
                            Select-Object -Property $(&$SelectProperty -InputObject $ErrRecord.Exception -Property $Property)
                    }
                }
                
                If ($Property -eq '*')
                {
                    # If all properties were chosen for display, then arrange them in the order
                    # the error object displays them by default.
                    If ($LogErrorRecordMsg)     {[array]$LogErrorMessageTmp += $LogErrorRecordMsg    }
                    If ($LogErrorInvocationMsg) {[array]$LogErrorMessageTmp += $LogErrorInvocationMsg}
                    If ($LogErrorExceptionMsg)  {[array]$LogErrorMessageTmp += $LogErrorExceptionMsg }
                }
                Else
                {
                    If ($LogErrorExceptionMsg)  {[array]$LogErrorMessageTmp += $LogErrorExceptionMsg }
                    If ($LogErrorRecordMsg)     {[array]$LogErrorMessageTmp += $LogErrorRecordMsg    }
                    If ($LogErrorInvocationMsg) {[array]$LogErrorMessageTmp += $LogErrorInvocationMsg}
                }
                
                If ($LogErrorMessageTmp)
                {
                    $LogErrorMessage = 'Error Record:'
                    $LogErrorMessage += "`n-------------"
                    $LogErrorMsg = $LogErrorMessageTmp | Format-List | Out-String
                    $LogErrorMessage += $LogErrorMsg
                }
                
                If ($GetErrorInnerException)
                {
                    # Capture Error Inner Exception(s)
                    If ($ErrRecord.Exception -and $ErrRecord.Exception.InnerException)
                    {
                        $LogInnerMessage = 'Error Inner Exception(s):'
                        $LogInnerMessage += "`n-------------------------"
                        
                        $ErrorInnerException = $ErrRecord.Exception.InnerException
                        $Count = 0
                        
                        While ($ErrorInnerException)
                        {
                            $InnerExceptionSeperator = "*" * 40
                            
                            $LogErrorInnerExceptionMsg = $ErrorInnerException |
                                Select-Object -Property $(&$SelectProperty -InputObject $ErrorInnerException -Property $Property) |
                                Format-List | Out-String
                            
                            If ($Count -gt 0) {$LogInnerMessage += $InnerExceptionSeperator}
                            $LogInnerMessage += $LogErrorInnerExceptionMsg
                            
                            $Count++
                            $ErrorInnerException = $ErrorInnerException.InnerException
                        }
                    }
                }
                
                If ($LogErrorMessage) {$Output += $LogErrorMessage}
                If ($LogInnerMessage) {$Output += $LogInnerMessage}
            }
        }
        End
        {
            Write-Output $Output
        }
    }