Can I get detailed exception stacktrace in PowerShell?

前端 未结 12 1118
暗喜
暗喜 2020-12-02 12:23

Runing such script:

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

I see

12条回答
  •  [愿得一人]
    2020-12-02 12:51

    Maybe I've misunderstood something, but my issue here is that I've not been seeing powershell script stack traces for inner exceptions.

    In the end I ended up searching $Global:Error for an exception's Error Eecord object to retrieve the script stack trace.

    <#
    .SYNOPSIS
       Expands all inner exceptions and provides Script Stack Traces where available by mapping Exceptions to ErrorRecords
    
    .NOTES
       Aggregate exceptions aren't full supported, and their child exceptions won't be traversed like regular inner exceptions
       
    #>
    function Get-InnerErrors ([Parameter(ValueFrompipeline)] $ErrorObject=$Global:Error[0])
    {
       # Get the first exception object from the input error
       $ex = $null
       if( $ErrorObject -is [System.Management.Automation.ErrorRecord] ){
           $ex = $ErrorObject.Exception 
       }
       elseif( $ErrorObject -is [System.Exception] ){
           $ex = $ErrorObject
       } 
       else
       {
           throw "Unexpected error type for Get-InnerErrors: $($ErrorObject.GetType()):`n $ErrorObject"
       }
    
       Write-Debug "Walking inner exceptions from exception"
       for ($i = 0; $ex; $i++, ($ex = $ex.InnerException))
       {  
           $ErrorRecord = $null
    
           if( $ex -is [System.Management.Automation.IContainsErrorRecord] ){ 
    
               Write-Debug "Inner Exception $i : Skipping search for ErrorRecord in `$Global:Error, exception type implements IContainsErrorRecord" 
               $ErrorRecord = ([System.Management.Automation.IContainsErrorRecord]$ex).ErrorRecord
           }
           else {
    
               # Find ErrorRecord for exception by mapping exception to ErrorRecrods in $Global:Error
    
               ForEach( $err in $Global:Error ){# Need to use Global scope when referring from a module
                   if( $err -is [System.Management.Automation.ErrorRecord] ){
                      if( $err.Exception -eq $ex ){
                           $ErrorRecord = $err 
                           Write-Debug "Inner Exception $i : Found ErrorRecord in `$Global:Error" 
                           break
                       }
                   } 
                   elseif( $err -is [System.Management.Automation.IContainsErrorRecord] ) {
                       if( $err -eq $ex -or $err.ErrorRecord.Exception -eq $ex ){
                           $ErrorRecord = $err.ErrorRecord
                           Write-Debug "Inner Exception $i : Found ErrorRecord in `$Global:Error" 
                           break
                       }
                   } 
                   else {
                       Write-Warning "Unexpected type in `$Global:Error[]. Expected `$Global:Error to always be an ErrorRecrod OR exception implementing IContainsErrorRecord but found type: $($err.GetType())"
                   }
               }
           }
    
           if( -not($ErrorRecord) ){
               Write-Debug "Inner Exception $i : No ErrorRecord could be found for exception of type: $($ex.GetType())" 
           }
           
          
           # Return details as custom object
    
           [PSCustomObject] @{
               ExceptionDepth      = $i
               Message             = $ex.Message
               ScriptStackTrace    = $ErrorRecord.ScriptStackTrace  # Note ErrorRecord will be null if exception was not from Powershell
               ExceptionType       = $ex.GetType().FullName
               ExceptionStackTrace = $ex.StackTrace
           }
       }
    }
    

    Example Usage:

    function Test-SqlConnection
    {
        $ConnectionString = "ThisConnectionStringWillFail"
        try{
            $sqlConnection = New-Object System.Data.SqlClient.SqlConnection $ConnectionString;
            $sqlConnection.Open();
        }
        catch
        {
            throw [System.Exception]::new("Sql connection failed with connection string: '$ConnectionString'", $_.Exception)
        }
        finally
        {
            if($sqlConnection){
                $sqlConnection.Close();
            }
        }
    }
    
    
    try{
        Test-SqlConnection
    }
    catch {
        Get-InnerErrors $_
    }
    

    Example output:

    
    
    ExceptionDepth      : 0
    Message             : Sql connection failed with connection string: 'ThisConnectionStringWillFail'
    ScriptStackTrace    : at Test-SqlConnection, : line 11
                          at , : line 23
    ExceptionType       : System.Exception
    ExceptionStackTrace : 
    
    ExceptionDepth      : 1
    Message             : Exception calling ".ctor" with "1" argument(s): "Format of the initialization string does not conform to specification starting at index 0."
    ScriptStackTrace    : 
    ExceptionType       : System.Management.Automation.MethodInvocationException
    ExceptionStackTrace :    at System.Management.Automation.DotNetAdapter.AuxiliaryConstructorInvoke(MethodInformation methodInformation, Object[] arguments, Object[] originalArguments)
                             at System.Management.Automation.DotNetAdapter.ConstructorInvokeDotNet(Type type, ConstructorInfo[] constructors, Object[] arguments)
                             at Microsoft.PowerShell.Commands.NewObjectCommand.CallConstructor(Type type, ConstructorInfo[] constructors, Object[] args)
    
    ExceptionDepth      : 2
    Message             : Format of the initialization string does not conform to specification starting at index 0.
    ScriptStackTrace    : 
    ExceptionType       : System.ArgumentException
    ExceptionStackTrace :    at System.Data.Common.DbConnectionOptions.GetKeyValuePair(String connectionString, Int32 currentPosition, StringBuilder buffer, Boolean useOdbcRules, String& keyname, String& 
                          keyvalue)
                             at System.Data.Common.DbConnectionOptions.ParseInternal(Hashtable parsetable, String connectionString, Boolean buildChain, Hashtable synonyms, Boolean firstKey)
                             at System.Data.Common.DbConnectionOptions..ctor(String connectionString, Hashtable synonyms, Boolean useOdbcRules)
                             at System.Data.SqlClient.SqlConnectionString..ctor(String connectionString)
                             at System.Data.SqlClient.SqlConnectionFactory.CreateConnectionOptions(String connectionString, DbConnectionOptions previous)
                             at System.Data.ProviderBase.DbConnectionFactory.GetConnectionPoolGroup(DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolOptions, DbConnectionOptions& userConnectionOptions)
                             at System.Data.SqlClient.SqlConnection.ConnectionString_Set(DbConnectionPoolKey key)
                             at System.Data.SqlClient.SqlConnection.set_ConnectionString(String value)
                             at System.Data.SqlClient.SqlConnection..ctor(String connectionString, SqlCredential credential)
    

提交回复
热议问题