我可以在PowerShell中获得详细的异常堆栈跟踪吗?

ale*_*2k8 49 powershell exception stack-trace

运行这样的脚本:

 1: function foo()
 2: {
 3:    bar
 4: }
 5: 
 6: function bar()
 7: {
 8:     throw "test"
 9: }
10: 
11: foo
Run Code Online (Sandbox Code Playgroud)

我知道了

test
At C:\test.ps1:8 char:10
Run Code Online (Sandbox Code Playgroud)

我可以获得详细的堆栈跟踪吗?

At bar() in C:\test.ps1:8
At foo() in C:\test.ps1:3 
At C:\test.ps1:11
Run Code Online (Sandbox Code Playgroud)

And*_*der 42

PowerShell团队博客上有一个名为Resolve-Error的功能,可以为您提供各种详细信息

请注意,$ error是您在PSSession中遇到的所有错误的数组.此功能将为您提供有关上次遇到的错误的详细信息.

function Resolve-Error ($ErrorRecord=$Error[0])
{
   $ErrorRecord | Format-List * -Force
   $ErrorRecord.InvocationInfo |Format-List *
   $Exception = $ErrorRecord.Exception
   for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException))
   {   "$i" * 80
       $Exception |Format-List * -Force
   }
}
Run Code Online (Sandbox Code Playgroud)

  • `$ ErrorRecord.InvocationInfo.PositionMessage`是最好的:) (8认同)

Joe*_*oey 17

有自动变量,$StackTrace但它似乎比内部PS细节更具体,而不是实际关心你的脚本,所以这将没有多大帮助.

还有Get-PSCallStack,但你打例外,不幸的的AS很快消失了.但是,您可以Get-PSCallStack在脚本中每次抛出之前放置一个.这样,您可以在遇到异常之前立即获得堆栈跟踪.

我认为可以通过使用Powershell的调试和跟踪功能来编写这样的功能,但我怀疑这很简单.

  • 我们应该提交一个增强请求(如果尚未提交),以自动添加到异常中. (4认同)
  • 如果我没有编写执行抛出操作的代码,则没有帮助:-( (2认同)

Tim*_*mbo 17

Powershell 3.0将一个ScriptStackTrace属性添加到ErrorRecord对象.我使用此函数进行错误报告:

function Write-Callstack([System.Management.Automation.ErrorRecord]$ErrorRecord=$null, [int]$Skip=1)
{
    Write-Host # blank line
    if ($ErrorRecord)
    {
        Write-Host -ForegroundColor Red "$ErrorRecord $($ErrorRecord.InvocationInfo.PositionMessage)"

        if ($ErrorRecord.Exception)
        {
            Write-Host -ForegroundColor Red $ErrorRecord.Exception
        }

        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 & skip the manual stack trace below
            Write-Host -ForegroundColor Red $ErrorRecord.ScriptStackTrace
            return
        }
    }

    Get-PSCallStack | Select -Skip $Skip | % {
        Write-Host -ForegroundColor Yellow -NoNewLine "! "
        Write-Host -ForegroundColor Red $_.Command $_.Location $(if ($_.Arguments.Length -le 80) { $_.Arguments })
    }
}
Run Code Online (Sandbox Code Playgroud)

Skip参数允许我从Get-PSCallstack列表中留下Write-Callstack或任意数量的错误处理堆栈帧.

请注意,如果从catch块调用,Get-PSCallstack将丢失throw站点和catch块之间的任何帧.因此,我更喜欢PS 3.0方法,即使每帧的细节较少.


Jas*_*her 12

您无法从PowerShell脚本代码的异常中获取堆栈跟踪,只能从.NET对象获取.为此,您需要获取Exception对象,如下所示:

$Error[0].Exception.StackTrace
$Error[0].Exception.InnerException.StackTrace
$Error[0].StackTrace
Run Code Online (Sandbox Code Playgroud)

  • $Error[0].ScriptStackTrace 是唯一一个给我任何有用的堆栈信息的。 (3认同)

use*_*200 9

我把我在这里找到的作为灵感,创造了一个很好的功能,任何人都可以放入他们的代码.

这就是我所说的:写主机"无法写入日志文件`n $(Resolve-Error)" - ForegroundColor Red

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: $global: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: Message, FullyQualifiedErrorId, ScriptStackTrace, PositionMessage, 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 is 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 $global: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

.NOTES
.LINK
#>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullorEmpty()]
        [array]$ErrorRecord,

        [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
    {
        ## If function was called without specifying an error record, then choose the latest error that occured
        If (-not $ErrorRecord)
        {
            If ($global:Error.Count -eq 0)
            {
                # The `$Error collection is empty
                Return
            }
            Else
            {
                [array]$ErrorRecord = $global:Error[0]
            }
        }

        ## Define script block for selecting and filtering the properties on the error object
        [scriptblock]$SelectProperty = {
            Param
            (
                [Parameter(Mandatory=$true)]
                [ValidateNotNullorEmpty()]
                $InputObject,

                [Parameter(Mandatory=$true)]
                [ValidateNotNullorEmpty()]
                [string[]]$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
        }

        # Initialize variables to avoid error if 'Set-StrictMode' is set
        $LogErrorRecordMsg      = $null
        $LogErrorInvocationMsg  = $null
        $LogErrorExceptionMsg   = $null
        $LogErrorMessageTmp     = $null
        $LogInnerMessage        = $null
    }
    Process
    {
        ForEach ($ErrRecord in $ErrorRecord)
        {
            ## Capture Error Record
            If ($GetErrorRecord)
            {
                [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrRecord -Property $Property
                $LogErrorRecordMsg = $ErrRecord | Select-Object -Property $SelectedProperties
            }

            ## Error Invocation Information
            If ($GetErrorInvocation)
            {
                If ($ErrRecord.InvocationInfo)
                {
                    [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrRecord.InvocationInfo -Property $Property
                    $LogErrorInvocationMsg = $ErrRecord.InvocationInfo | Select-Object -Property $SelectedProperties
                }
            }

            ## Capture Error Exception
            If ($GetErrorException)
            {
                If ($ErrRecord.Exception)
                {
                    [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrRecord.Exception -Property $Property
                    $LogErrorExceptionMsg = $ErrRecord.Exception | Select-Object -Property $SelectedProperties
                }
            }

            ## Display properties in the correct order
            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
            {
                # Display selected properties in our custom order
                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
            }

            ## Capture Error Inner Exception(s)
            If ($GetErrorInnerException)
            {
                If ($ErrRecord.Exception -and $ErrRecord.Exception.InnerException)
                {
                    $LogInnerMessage  = 'Error Inner Exception(s):'
                    $LogInnerMessage += "`n-------------------------"

                    $ErrorInnerException = $ErrRecord.Exception.InnerException
                    $Count = 0

                    While ($ErrorInnerException)
                    {
                        $InnerExceptionSeperator = '~' * 40

                        [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrorInnerException -Property $Property
                        $LogErrorInnerExceptionMsg = $ErrorInnerException | Select-Object -Property $SelectedProperties | Format-List | Out-String

                        If ($Count -gt 0)
                        {
                            $LogInnerMessage += $InnerExceptionSeperator
                        }
                        $LogInnerMessage += $LogErrorInnerExceptionMsg

                        $Count++
                        $ErrorInnerException = $ErrorInnerException.InnerException
                    }
                }
            }

            If ($LogErrorMessage) { $Output += $LogErrorMessage }
            If ($LogInnerMessage) { $Output += $LogInnerMessage }

            Write-Output $Output

            If (Test-Path -Path 'variable:Output'            ) { Clear-Variable -Name Output             }
            If (Test-Path -Path 'variable:LogErrorMessage'   ) { Clear-Variable -Name LogErrorMessage    }
            If (Test-Path -Path 'variable:LogInnerMessage'   ) { Clear-Variable -Name LogInnerMessage    }
            If (Test-Path -Path 'variable:LogErrorMessageTmp') { Clear-Variable -Name LogErrorMessageTmp }
        }
    }
    End {}
}
Run Code Online (Sandbox Code Playgroud)