2009-04-28 9 views

Répondre

14

Il est la variable automatique $StackTrace mais il semble être un peu plus spécifique aux détails PS internes que se soucier réellement de votre script, de sorte que ce ne sera pas d'un grand secours.

Il y a aussi Get-PSCallStack mais c'est parti dès que vous avez atteint l'exception, malheureusement. Vous pouvez cependant mettre un Get-PSCallStack avant chaque lancer dans votre script. De cette façon, vous obtenez une trace de pile immédiatement avant de cliquer sur une exception.

Je pense qu'on pourrait écrire une telle fonctionnalité en utilisant les fonctionnalités de débogage et de traçage de Powershell mais je doute que ce soit facile.

+4

Nous devrions déposer une demande d'amélioration (si elle n'a pas déjà été soumise) pour que cela soit ajouté automatiquement aux exceptions. – JasonMArcher

+0

Cette fonctionnalité a été ajoutée dans PS 3.0. J'ai posté une réponse avec un exemple de code. – Timbo

+0

Ne aide pas si je n'ai pas écrit le code en faisant le lancement :-( – bacar

32

Il y a un function up on the PowerShell Team blog appelé Resolve-erreur qui vous permet d'obtenir toutes sortes de détails

Notez que $ erreur est un tableau de toutes les erreurs que vous avez rencontrées dans votre PSSession. Cette fonction vous donnera des détails sur la dernière erreur que vous avez rencontrée.

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' est le meilleur :) –

+0

Je me demande si $ _. Exception.InnerException.Message fonctionnerait? – Michele

+0

C'est ce que j'ai dans une instruction catch avec catch [Exception] – Michele

9

Vous ne pouvez pas obtenir une trace de pile à partir d'exceptions du code de scripts PowerShell, uniquement à partir d'objets .NET. Pour ce faire, vous aurez besoin d'obtenir l'objet d'exception comme l'un de ceux-ci:

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

$ Erreur [0] .ScriptStackTrace est le seul qui me donne des informations utiles sur la pile. – rob

2

Voici une façon: Tracing the script stack

Le cœur de c'est ce code:

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

Le lien semble être mort. –

+1

@BenThul: Correction. –

15

Powershell 3.0 ajoute une propriété ScriptStackTrace à l'objet ErrorRecord. J'utilise cette fonction pour les rapports d'erreur:

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

Le paramètre Ignorer me permet de laisser en écriture ou tout Callstack nombre d'images de la pile de gestion des erreurs sur la liste Get-PSCallStack.

Notez que si vous appelez à partir d'un bloc catch, Get-PSCallstack manquera toutes les trames entre le site de lancement et le bloc catch. Je préfère donc la méthode PS 3.0 même si nous avons moins de détails par image.

+0

Que fait la commande "Trace"? Je reçois une erreur que l'on ne trouve pas, c'est quelque chose que tu as écrit? –

+0

Oui, Trace est une fonction interne qui écrit dans un fichier journal. Remplacez-la par la méthode de journalisation de votre choix, peut-être Write-Host. – Timbo

7

J'ai pris ce que j'ai trouvé ici comme source d'inspiration et créé une fonction agréable que n'importe qui peut laisser tomber dans son code.

Voilà comment je l'appelle: Write-Host "Impossible d'écrire dans le fichier journal` n $ (Resolve-erreur)" -ForegroundColor rouge

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

Vous pouvez également modifier le formatage par défaut pour l'objet d'erreur à inclure la trace de la pile. Fondamentalement, faites votre fichier de format en copiant le morceau pour System.Management.Automation.ErrorRecord de $ PSHOME \ PowerShellCore.format.ps1xml et ajoutez votre propre élément qui ajoute la trace.Puis chargez-le avec Update-FormatData. Pour plus de détails, je viens d'écrire un billet de blog à ce sujet: https://blogs.msdn.microsoft.com/sergey_babkins_blog/2016/12/28/getting-a-stack-trace-in-powershell/

Oh, encore une chose: cela ne se propage pas automatiquement dans les sessions à distance. Les objets sont formatés en chaînes sur le côté distant. Pour les traces de pile dans les sessions à distance, vous devrez télécharger ce fichier là-bas et appeler à nouveau Update-FormatData.

+1

Vous devez changer la configuration sur chaque machine, celle qui exécute le script. Le comportement préféré est d'avoir le script, qui s'exécute de manière cohérente, pas "sur ma machine" seulement. –

0

Je viens de comprendre. Le $ _ est l'exception captée dans le bloc catch.

$errorString= $_ | Out-String 
Questions connexes