Поймать полное сообщение об исключении

Рассмотрим:

Invoke-WebRequest $sumoApiURL -Headers @{"Content-Type"= "application/json"} -Credential $cred -WebSession $webRequestSession -Method post -Body $sumojson -ErrorAction Stop

Это вызывает следующее исключение:

Enter image description here

Как я могу поймать его полностью или хотя бы отфильтровать "Ресурс с таким именем уже существует"?

Использование $_.Exception.GetType().FullName дает

System.Net.WebException

и $_.Exception.Message дает

Удаленный сервер возвратил ошибку: (400) Bad Request.

Ответы

Ответ 1

Ошибки и исключения в PowerShell являются структурированными объектами. Сообщение об ошибке, напечатанное на консоли, на самом деле является отформатированным сообщением с информацией из нескольких элементов объекта "ошибка/исключение". Вы можете (re-) построить его самостоятельно так:

$formatstring = "{0} : {1}'n{2}'n" +
                "    + CategoryInfo          : {3}'n" +
                "    + FullyQualifiedErrorId : {4}'n"
$fields = $_.InvocationInfo.MyCommand.Name,
          $_.ErrorDetails.Message,
          $_.InvocationInfo.PositionMessage,
          $_.CategoryInfo.ToString(),
          $_.FullyQualifiedErrorId

$formatstring -f $fields

Если вы просто хотите, чтобы сообщение об ошибке отображалось в вашем блоке catch, вы можете просто повторить текущую переменную объекта (которая содержит ошибку в этой точке):

try {
  ...
} catch {
  $_
}

Если вам нужен цветной вывод, используйте Write-Host с отформатированной строкой, как описано выше:

try {
  ...
} catch {
  ...
  Write-Host -Foreground Red -Background Black ($formatstring -f $fields)
}

С учетом вышесказанного, обычно вы не хотите просто отображать сообщение об ошибке как есть в обработчике исключений (в противном случае -ErrorAction Stop будет бессмысленным). Структурированные объекты ошибок/исключений предоставляют вам дополнительную информацию, которую вы можете использовать для лучшего контроля ошибок. Например, у вас есть $_.Exception.HResult с фактическим номером ошибки. $_.ScriptStackTrace и $_.Exception.StackTrace, так что вы можете отображать трассировки стека при отладке. $_.Exception.InnerException предоставляет вам доступ к вложенным исключениям, которые часто содержат дополнительную информацию об ошибке (ошибки PowerShell верхнего уровня могут быть несколько общими). Вы можете развернуть эти вложенные исключения примерно так:

$e = $_.Exception
$msg = $e.Message
while ($e.InnerException) {
  $e = $e.InnerException
  $msg += "'n" + $e.Message
}
$msg

В вашем случае информация, которую вы хотите извлечь, кажется, находится в $_.ErrorDetails.Message. Мне не совсем понятно, есть ли у вас объект или строка JSON, но вы должны быть в состоянии получить информацию о типах и значениях членов $_.ErrorDetails, запустив

$_.ErrorDetails | Get-Member
$_.ErrorDetails | Format-List *

Если $_.ErrorDetails.Message является объектом, вы сможете получить строку сообщения, подобную этой:

$_.ErrorDetails.Message.message

в противном случае вам нужно сначала преобразовать строку JSON в объект:

$_.ErrorDetails.Message | ConvertFrom-Json | Select-Object -Expand message

В зависимости от того, какую ошибку вы обрабатываете, исключения из определенных типов могут также включать более конкретную информацию о проблеме. Например, в вашем случае у вас есть WebException, который в дополнение к сообщению об ошибке ($_.Exception.Message) содержит фактический ответ от сервера:

PS C:\> $e.Exception | Get-Member

   TypeName: System.Net.WebException

Name             MemberType Definition
----             ---------- ----------
Equals           Method     bool Equals(System.Object obj), bool _Exception.E...
GetBaseException Method     System.Exception GetBaseException(), System.Excep...
GetHashCode      Method     int GetHashCode(), int _Exception.GetHashCode()
GetObjectData    Method     void GetObjectData(System.Runtime.Serialization.S...
GetType          Method     type GetType(), type _Exception.GetType()
ToString         Method     string ToString(), string _Exception.ToString()
Data             Property   System.Collections.IDictionary Data {get;}
HelpLink         Property   string HelpLink {get;set;}
HResult          Property   int HResult {get;}
InnerException   Property   System.Exception InnerException {get;}
Message          Property   string Message {get;}
Response         Property   System.Net.WebResponse Response {get;}
Source           Property   string Source {get;set;}
StackTrace       Property   string StackTrace {get;}
Status           Property   System.Net.WebExceptionStatus Status {get;}
TargetSite       Property   System.Reflection.MethodBase TargetSite {get;}

которая предоставляет вам такую информацию:

PS C:\> $e.Exception.Response

IsMutuallyAuthenticated : False
Cookies                 : {}
Headers                 : {Keep-Alive, Connection, Content-Length, Content-T...}
SupportsHeaders         : True
ContentLength           : 198
ContentEncoding         :
ContentType             : text/html; charset=iso-8859-1
CharacterSet            : iso-8859-1
Server                  : Apache/2.4.10
LastModified            : 17.07.2016 14:39:29
StatusCode              : NotFound
StatusDescription       : Not Found
ProtocolVersion         : 1.1
ResponseUri             : http://www.example.com/
Method                  : POST
IsFromCache             : False

Поскольку не все исключения имеют одинаковый набор свойств, вы можете использовать определенные обработчики для определенных исключений:

try {
  ...
} catch [System.ArgumentException] {
  # handle argument exceptions
} catch [System.Net.WebException] {
  # handle web exceptions
} catch {
  # handle all other exceptions
}

Если у вас есть операции, которые необходимо выполнить независимо от того, произошла ошибка или нет (задачи очистки, такие как закрытие сокета или соединение с базой данных), вы можете поместить их в блок finally после обработки исключения:

try {
  ...
} catch {
  ...
} finally {
  # cleanup operations go here
}

Ответ 2

Я нашел его!

Просто распечатайте $Error[0] для последнего сообщения об ошибке.

Ответ 3

Вы можете добавить:

-ErrorVariable errvar

А затем посмотрите в $errvar.

Ответ 4

Следующее хорошо сработало для меня

try {
    asdf
} catch {
    $string_err = $_ | Out-String
}

write-host $string_err

Результатом этого является следующая строка вместо объекта ErrorRecord

asdf : The term 'asdf' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\TASaif\Desktop\tmp\catch_exceptions.ps1:2 char:5
+     asdf
+     ~~~~
    + CategoryInfo          : ObjectNotFound: (asdf:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Ответ 5

Я продолжаю возвращаться к этим вопросам, пытаясь выяснить, где именно те данные, которые меня интересуют, скрыты в действительно монолитной структуре ErrorRecord. Почти все ответы дают пошаговые инструкции о том, как извлекать определенные биты данных.

Но я обнаружил, что очень полезно выгрузить весь объект с помощью ConvertTo-Json, чтобы я мог визуально видеть ВСЕ ГРАМОТНО в понятной компоновке.

    try {
        Invoke-WebRequest...
    }
    catch {
        Write-Host ($_ | ConvertTo-Json)
    }

Используйте параметр ConvertTo-Json -Depth для расширения более глубоких значений, но соблюдайте предельную осторожность, проходя глубину по умолчанию 2:

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-json