2009-04-28 34 views
37

runingて、このようなスクリプト:私はPowerShellで詳細なスタックトレースを取得できますか?

test 
At C:\test.ps1:8 char:10 

は、私が代わりに詳細なスタックトレースを取得することができます参照してください

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

At bar() in C:\test.ps1:8 
At foo() in C:\test.ps1:3 
At C:\test.ps1:11 

答えて

14

は自動変数$StackTraceありますが、それは非常に助けになることはありませんので、実際にあなたのスクリプトを気により内部PSの詳細をもう少し具体的であるように思われます。

Get-PSCallStackありますが、あなたが、残念ながら、例外を襲ったとして、それは、すぐになくなっています。ただし、スクリプトに投げるたびにGet-PSCallStackを入れることができます。そうすれば、例外が発生する直前にスタックトレースを取得できます。

私は1つのデバッグを使用してのPowershellの機能をトレースすることにより、スクリプトなど機能できると思いますが、私はそれが簡単になるだろう疑い。

+4

拡張要求(まだ送信されていない場合)を、例外に自動的に追加する必要があります。 – JasonMArcher

+0

この機能はPS 3.0に追加されました。私はサンプルコードで回答を掲載しました。 – Timbo

+0

私は投げているコードを書かなかった場合:-( – bacar

32

はあなたの詳細

$エラーがあなたのPSSessionで遭遇したすべてのエラーの配列です

ノートのすべての種類を取得しますfunction up on the PowerShell Team blogと呼ばれる解決-エラーがあります。この関数は、直前に発生したエラーの詳細を表示します。

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 
    } 
} 
+5

'$ ErrorRecord.InvocationInfo.PositionMessage'が最高です:) –

+0

$ _.Exception.InnerException.Messageが動作するのだろうかと思いますか? – Michele

+0

それは私がキャッチステートメントでキャッチで持っているものです[例外] – Michele

9

スタックトレースは、PowerShellのスクリプトの例外から.NETオブジェクトからのみ取得することはできません。これを行うには、次のいずれかのような例外オブジェクトを取得する必要があります。

$Error[0].Exception.StackTrace 
$Error[0].Exception.InnerException.StackTrace 
$Error[0].StackTrace 
+1

$エラー[0] .ScriptStackTraceは私に有用なスタック情報を与える唯一のものです。 – rob

2

ここでの方法です:それのTracing the script stack

コアは、このコードです:

 
    1..100 | %{ $inv = &{ gv -sc $_ myinvocation } 
+0

リンクが死んでいるようです。 –

+1

@BenThul:修正されました。 –

15

の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 }) 
    } 
} 

スキップパラメータは、私が書く、コールスタックまたはGET-PSCallstackリストのうち、エラー処理のスタック・フレームの任意の数を残すことができます。 catchブロックから呼び出された場合は、Get-PSCallstackがスローサイトとcatchブロックの間の任意のフレームを欠場すること

は注意してください。したがって私はフレームあたりの詳細が少ないにもかかわらず、PS 3.0の方が好きです。

+0

"トレース"コマンドは何をしますか?それが見つからないというエラーが表示されます。あなたが書いたものですか? –

+0

はい、トレースは、ログファイルに書き込む内部関数です。選択したロギング方法(おそらくWrite-Host)に置き換えてください。 – Timbo

7

私はここでインスピレーションを得て、誰でも自分のコードに入れることができる素晴らしい機能を作りました。

これは私がそれを呼び出す方法です: 書き込みホストは、「ログファイルへの書き込みに失敗しました `N $(解決-エラー)」-foregroundcolorレッド

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 {} 
} 
0

また、既定の書式設定を変更することができますスタックトレースを含むエラーオブジェクト。基本的には、$ PSHOME \ PowerShellCore.format.ps1xmlのSystem.Management.Automation.ErrorRecordのチャンクをコピーしてフォーマットファイルを作成し、トレースを追加する独自の 要素を追加します。それから、Update-FormatDataでロードします。詳細については、私はちょうどそれについてのブログ記事を書いた:https://blogs.msdn.microsoft.com/sergey_babkins_blog/2016/12/28/getting-a-stack-trace-in-powershell/

ああ、もう1つ:これは自動的にリモートセッションに伝播しません。オブジェクトはリモート側の文字列にフォーマットされます。リモートセッションのスタックトレースの場合は、このファイルをそこにアップロードし、そこでUpdate-FormatDataを再度呼び出す必要があります。

+1

スクリプトを実行している各マシンの設定を変更する必要があります。推奨される動作は、「自分のマシン上」ではなく、一貫して実行されるスクリプトを持つことです。 –

0

私はただそれを理解しました。 $ _はcatchブロックで捕捉された例外です。

$errorString= $_ | Out-String 
関連する問題