2009-04-28 14 views

Antwort

14

Es ist die automatische Variable $StackTrace aber es scheint ein wenig mehr spezifisch für interne PS Details zu sein, als es eigentlich um das Skript kümmern, so dass keine große Hilfe sein wird.

Es gibt auch Get-PSCallStack, aber das ist weg, sobald Sie die Ausnahme getroffen haben, leider. Sie können jedoch vor jedem Wurf in Ihrem Skript eine Get-PSCallStack setzen. Auf diese Weise erhalten Sie direkt vor dem Auftreten einer Ausnahme eine Stack-Kurve.

Ich denke, man könnte solche Funktionen mithilfe der Debugging- und Tracing-Funktionen von Powershell skripten, aber ich bezweifle, dass es einfach wäre.

+4

Wir sollten eine Erweiterungsanforderung einreichen (falls diese noch nicht eingereicht wurde), damit diese automatisch zu Ausnahmen hinzugefügt wird. – JasonMArcher

+0

Diese Funktionalität wurde in PS 3.0 hinzugefügt. Ich habe eine Antwort mit Beispielcode gepostet. – Timbo

+0

Hilft nicht, wenn ich nicht den Code geschrieben habe, der das Werfen tut :-( – bacar

32

Es gibt eine function up on the PowerShell Team blog genannt Resolve-Fehler, die Sie alle Arten von Details

Hinweis erhalten, die $ Fehler ist ein Array aller Fehler, die Sie in Ihrem PSSession begegnet sind. Diese Funktion gibt Ihnen Details zum letzten Fehler, den Sie festgestellt haben.

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' ist das beste :) –

+0

Ich frage mich, ob $ _. Exception.InnerException.Message funktionieren würde? – Michele

+0

Das habe ich in einer catch-Anweisung mit catch [Exception] – Michele

9

Sie können einen Stack-Trace nicht von Ausnahmen des PowerShell-Codes von Scripts abrufen, sondern nur von .NET-Objekten. Um das zu tun, müssen Sie das Exception-Objekt wie eine von ihnen zu bekommen:

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

$ Error [0] .ScriptStackTrace ist der einzige, der mir nützliche Stack-Informationen gibt. – rob

2

Hier ist eine Art und Weise: Tracing the script stack

Der Kern davon ist dieser Code:

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

Link scheint tot zu sein. –

+1

@BenThul: Behoben. –

15

Powershell 3.0 Fügt dem ErrorRecord-Objekt eine ScriptStackTrace-Eigenschaft hinzu. Ich benutze diese Funktion für die Fehlerberichterstattung:

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

Der überspringen Parameter läßt mich verlassen Write-Aufrufhierarchie oder eine beliebige Anzahl von Fehlerbehandlungs Stack-Frames aus der Get-PsCallStack Auflistung.

Beachten Sie, dass Get-PSCallstack alle Frames zwischen der Werbeseite und dem catch-Block verpasst, wenn sie von einem catch-Block aufgerufen werden. Daher bevorzuge ich die PS 3.0-Methode, obwohl wir weniger Details pro Frame haben.

+0

Was macht der "Trace" -Befehl? Ich bekomme einen Fehler, dass es nicht gefunden wird, ist das etwas, was du geschrieben hast? –

+0

Ja, Trace ist eine interne Funktion, die in eine Protokolldatei schreibt. Ersetzen Sie sie durch Ihre Protokollierungsmethode, vielleicht Write-Host. – Timbo

7

Ich habe das, was ich hier gefunden habe, als Inspiration genommen und eine nette Funktion geschaffen, die jeder in seinen Code einfügen kann. Diese

ist, wie ich es nennen: Write-Host "Fehler in der Protokolldatei` n $ (Resolve-Error) schreiben" -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 {} 
} 
0

Sie auch die Standardeinstellung für die Formatierung ändern das Fehlerobjekt, um den Stack-Trace einzuschließen. Erstellen Sie im Grunde Ihre Formatdatei, indem Sie den Chunk für System.Management.Automation.ErrorRecord aus $ PSHOME \ PowerShellCore.format.ps1xml kopieren und Ihr eigenes Element hinzufügen, das die Ablaufverfolgung hinzufügt.Laden Sie es dann mit Update-FormatData. Für weitere Details habe ich gerade einen Blog-Post darüber geschrieben: https://blogs.msdn.microsoft.com/sergey_babkins_blog/2016/12/28/getting-a-stack-trace-in-powershell/

Oh, noch etwas: Dies wird nicht automatisch in die Remote-Sitzungen weitergegeben. Die Objekte werden auf der Remoteseite in Zeichenfolgen formatiert. Für Stack-Traces in den Remote-Sitzungen müssen Sie diese Datei dort hochladen und dort Update-FormatData erneut aufrufen.

+1

Sie müssen die Konfiguration auf jedem Computer ändern, auf dem das Skript ausgeführt wird. Das bevorzugte Verhalten besteht darin, das Skript, das konsistent ausgeführt wird, nicht nur auf meinem Computer zu haben. –

0

Ich habe es gerade herausgefunden. $ _ Ist die Ausnahme, die im catch-Block abgefangen wird.

$errorString= $_ | Out-String 
Verwandte Themen