PowerShell에서 자세한 예외 스택 추적을 받을 수 있습니까?
다음 스크립트 실행:
1: function foo()
2: {
3: bar
4: }
5:
6: function bar()
7: {
8: throw "test"
9: }
10:
11: foo
그렇군요.
test
At C:\test.ps1:8 char:10
대신 자세한 스택 트레이스를 받을 수 있을까요?
At bar() in C:\test.ps1:8
At foo() in C:\test.ps1:3
At C:\test.ps1:11
PowerShell Team 블로그에는 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
}
}
자동 변수가 있습니다.$StackTrace
하지만 실제로 대본에 신경쓰는 것보다 내부 PS 세부사항에 좀 더 구체적인 것 같아서 큰 도움이 되지 않을 것 같습니다.
또한 있습니다.Get-PSCallStack
불행하게도 예외를 두자마자 사라졌죠하지만, 당신은, 당신은,Get-PSCallStack
대본을 쓰기 전에 말입니다.이렇게 하면 예외가 발생하기 직전에 스택 추적을 받을 수 있습니다.
파워셸의 디버깅과 추적 기능을 이용하면 그런 기능을 스크립팅할 수 있을 것 같은데 쉽진 않을 것 같습니다.
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 })
}
}
Skip 매개 변수를 사용하면 Write-Call stack 또는 임의 수의 오류 처리 스택 프레임을 Get-PSCall stack 목록에서 제외할 수 있습니다.
캐치 블록에서 호출되는 경우 Get-PSCallstack은 스로우 사이트와 캐치 블록 사이의 프레임을 모두 놓칩니다.따라서 프레임당 세부사항은 적지만 PS 3.0 방식을 선호합니다.
에서만 PowerShell 스크립트 코드의 예외에서 스택 추적을 가져올 수 없습니다.NET 개체.이를 위해서는 다음 중 하나와 같은 Exception 개체를 가져와야 합니다.
$Error[0].Exception.StackTrace
$Error[0].Exception.InnerException.StackTrace
$Error[0].StackTrace
여기서 발견한 것을 영감으로 삼아 누구나 코드에 넣을 수 있는 좋은 함수를 만들었습니다.
이렇게 부릅니다. 쓰기-호스트 "로그 파일 '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 {}
}
코드:
try {
...
}
catch {
Write-Host $_.Exception.Message -Foreground "Red"
Write-Host $_.ScriptStackTrace -Foreground "DarkGray"
exit 1
}
다음 형식으로 오류를 반향합니다.
No match was found for the specified search criteria and module names 'psake'.
at Get-InstalledModule<Process>, ...\PSModule.psm1: line 9251
at Import-ModuleThirdparty, ...\Import-ModuleThirdparty.psm1: line 3
at <ScriptBlock>, ...\index.ps1: line 13
방법은 다음과 같습니다.스크립트 스택 추적
그 핵심은 다음과 같은 코드입니다.
1..100 | %{ $inv = &{ gv -sc $_ my invocation }
난 방금 알아냈다.$_은 캐치 블록에 포함된 예외입니다.
$errorString= $_ | Out-String
또한 스택 추적을 포함하도록 오류 개체의 기본 형식을 변경할 수도 있습니다.기본적으로 시스템용 청크를 복사하여 포맷 파일을 만듭니다.관리.자동화.$PSHOME\PowerShellCore.format.ps1xml에서 오류를 기록하고 추적을 추가하는 고유 요소를 추가합니다.그런 다음 Update-Format Data와 함께 로드합니다.더 자세한 것은, 저는 방금 그것에 대한 블로그 포스트를 썼습니다: https://blogs.msdn.microsoft.com/sergey_babkins_blog/2016/12/28/getting-a-stack-trace-in-powershell/
아, 한 가지 더 말씀 드리자면 원격 세션에 자동으로 전파되지 않는다는 것입니다.개체는 원격에서 문자열로 포맷됩니다.원격 세션의 스택 트레이스에 대해서는 해당 파일을 업로드한 후 해당 파일에서 Update-FormatData를 다시 호출해야 합니다.
내장 솔루션을 찾고 있던 중 우연히 발견했습니다.간단한 해결책으로 진행하겠습니다.파워셸을 사용하기 전에 트레이스 블록만 추가하면 됩니다.그러면 콜 스택이 표시됩니다.아래쪽은 오류 메시지 전에 스택이 표시된다는 것입니다.
Trace {
$_.ScriptStackTrace
}
제가 뭔가 잘못 이해했는지도 모르지만, 여기서 제 문제는 내부 예외에 대한 파워셸 스크립트 스택 추적을 보지 못했다는 것입니다.
결국 $Global을 검색하게 되었습니다.예외의 Error Eecord 개체가 스크립트 스택 추적을 검색하는 동안 오류가 발생했습니다.
<#
.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
}
}
}
사용 예시:
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 $_
}
출력 예시:
ExceptionDepth : 0
Message : Sql connection failed with connection string: 'ThisConnectionStringWillFail'
ScriptStackTrace : at Test-SqlConnection, <No file>: line 11
at <ScriptBlock>, <No file>: 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)
호출이나 PowerShell 역추적을 ..Invoke()
을 위해서, . 는.Set-PSDebug -Trace 2
도움이 될 겁니다 인 스크립트의합니다.실행 중인 스크립트의 모든 행을 인쇄합니다.
#(를 #(1) (2) 를를 .WrapStackTraceLog({ function f{ 1/0 } ; & f }) # let's divide by zero
Function WrapStackTraceLog($func) {
try {
# return $func.Invoke($args) # (1)
return (& $func $args) # (2)
} catch {
Write-Host ('=' * 70)
Write-Host $_.Exception.Message
Write-Host ('-' * 70)
Write-Host $_.ScriptStackTrace
Write-Host ('-' * 70)
Write-Host "$StackTrace"
Write-Host ('=' * 70)
}
}
분기(1) 예외가 발견됨:
Exception calling "Invoke" with "1" argument(s): "Attempted to divide by zero."
분기(2)가 더 유용합니다.
at f, <No file>: line 1
at <ScriptBlock>, <No file>: line 1
at global:WrapStackTraceLog, <No file>: line 4
at <ScriptBlock>, <No file>: line 1
그러나 분기(1): 추적을 통해 Invokes를 추적할 수 있습니다.
DEBUG: ! CALL function 'f'
DEBUG: 1+ WrapStackTraceLog({ function f{ >>>> 1/0 } ; & f })
DEBUG: 6+ >>>> Write-Host ('=' * 70)
======================================================================
DEBUG: 7+ >>>> Write-Host $_.Exception.Message
Exception calling "Invoke" with "1" argument(s): "Attempted to divide by zero."
언급URL : https://stackoverflow.com/questions/795751/can-i-get-detailed-exception-stacktrace-in-powershell
'source' 카테고리의 다른 글
C에서 카레를 할 수 있는 방법이 있습니까? (0) | 2023.09.26 |
---|---|
워드프레스 API json, 한 번의 게시물에서 사전 게시물과 다음 게시물을 얻는 방법은? (0) | 2023.09.26 |
Oracle 기존 테이블에 날짜 열 추가(기본값은 새 행만 sysdate) (0) | 2023.09.26 |
DOM 요소 내부의 텍스트 라인을 세는 방법은 무엇입니까?내가 할 수 있을까? (0) | 2023.09.26 |
TypeError: e.preventDefault가 함수가 아닙니다. (0) | 2023.09.26 |