DPXone
Lieutenant
- Registriert
- Mai 2009
- Beiträge
- 554
Hi Forum,
wollte mal eine Möglichkeit in PowerShell vorstellen, mit welcher man leicht sehr detaillierte Fehlermeldungen bei Script-Fehlern ausgeben kann.
Das Ganze kann man dann z. B. per Send-MailMessage ganz leicht an sein Postfach senden lassen und/oder in das Windows-Eventlog (Write-EventLog) schreiben kann (zur Nachverfolgung).
Bei Verbesserungsvorschlägen, Fragen oder Anregungen einfach hier schreiben.
Anmerkung:
Ihr solltet bei allen Cmdlets (in C# progammierte Funktionen für PowerShell als DLL; = alle von Microsoft bereitgestellten Funktionen in Modulen) immer die Error-Action auf Stop setzen ( -ErrorAction Stop .
Manche haben per Default nämlich Continue als ErrorAction, im Sinne von: Gib einen Error in der Konsole aus, verursache aber keinen Error an sich (Throw "Error") , was die Try-Catch-Methode dann nicht abgreift und somit die Funktion von mir nicht greift.
Bei eigenen Funktionen bitte [CmdletBinding()] einbauen (siehe Beispiel unten; Zeile 47), sonst gibt es diesen Parameter nicht.
Error-Output:
Man beachte insbesondere den ScriptStackTrace, welche auf die Zeile des Funktions-Aufrufes im Script (58), als auch auf die verursachende Zeile in der Funktion (52) aufmerksam macht!
Anders Error-Output Beispiel mit WinSCP .NET Assembly:
wollte mal eine Möglichkeit in PowerShell vorstellen, mit welcher man leicht sehr detaillierte Fehlermeldungen bei Script-Fehlern ausgeben kann.
Das Ganze kann man dann z. B. per Send-MailMessage ganz leicht an sein Postfach senden lassen und/oder in das Windows-Eventlog (Write-EventLog) schreiben kann (zur Nachverfolgung).
Bei Verbesserungsvorschlägen, Fragen oder Anregungen einfach hier schreiben.
Anmerkung:
Ihr solltet bei allen Cmdlets (in C# progammierte Funktionen für PowerShell als DLL; = alle von Microsoft bereitgestellten Funktionen in Modulen) immer die Error-Action auf Stop setzen ( -ErrorAction Stop .
Manche haben per Default nämlich Continue als ErrorAction, im Sinne von: Gib einen Error in der Konsole aus, verursache aber keinen Error an sich (Throw "Error") , was die Try-Catch-Methode dann nicht abgreift und somit die Funktion von mir nicht greift.
Bei eigenen Funktionen bitte [CmdletBinding()] einbauen (siehe Beispiel unten; Zeile 47), sonst gibt es diesen Parameter nicht.
Code:
Function Out-Error {
Param (
[Parameter(Mandatory = $true , ValueFromPipeline = $true)]
[System.Management.Automation.ErrorRecord[]] $ErrorObjects # Input-Object
)
Begin {
[string[]] $Message = @() # Deklariere ein String-Array für die ErrorMessage (erlaubt Addition)
$CrLf = "`r`n" # = Carriage-Return (Cr) + Line-Feed (Lf) als Variable (=Neue Zeile)
$Tab = "`t" # = Tabstop
}
Process {
Foreach ($ErrorObject In $ErrorObjects) { # Falls es mehrere Objecte in der Pipeline gibt; Bei $ErrorObject aber selten/garnicht der Fall
$Message+= '-' * 200 + $CrLf # Header - # Füge ein '-' als Erkennungs- und Trennzeichenmerkmal an
$Message+= $ErrorObject.Exception.Message + ($CrLf * 2) # Header - Kurzer Error-Text
$Message+= ( # Body
(
(
(
$ErrorObject | Select @{ n = 'CategoryInfo' ; e = {($_.CategoryInfo | fl * -Force | Out-String).trim() } } , # Format-List -Force -> um mehr Informationen zu erhalten (PowerShell gibt sonst nicht alle Infos aus)
@{ n = 'ErrorDetails' ; e = {($_.ErrorDetails | fl * | Out-String).trim() } } ,
@{ n = 'Exception' ; e = {($_.Exception | fl * -Force | Out-String).trim() } } , # Format-List -Force -> um mehr Informationen zu erhalten (PowerShell gibt sonst nicht alle Infos aus)
@{ n = 'FullyQualifiedErrorId' ; e = {($_.FullyQualifiedErrorId | fl * | Out-String).trim() } } ,
@{ n = 'InvocationInfo' ; e = {($_.InvocationInfo | fl * -Force | Out-String).trim() } } , # Format-List -Force -> um mehr Informationen zu erhalten (PowerShell gibt sonst nicht alle Infos aus)
@{ n = 'PipelineIterationInfo' ; e = {($_.PipelineIterationInfo | fl * | Out-String).trim() } } ,
@{ n = 'PSMessageDetails' ; e = {($_.PSMessageDetails | fl * | Out-String).trim() } } ,
@{ n = 'ScriptStackTrace' ; e = {($_.ScriptStackTrace | fl * | Out-String).trim() } } ,
@{ n = 'TargetObject' ; e = {($_.TargetObject | fl * | Out-String).trim() } } , * -ErrorAction Ignore
) | format-list * | out-string # Formatiere als Liste und erstelle ein String aus dem Object
).trim() # Entferne führende (leading) und endende (trailing) Leerzeichen/Leerzeilen (das Object gibt beim Umwandeln zum String immer beides aus)
) -split "`r`n" | % { "$($Tab*2)$_" } # Splitte den erstellten (Komplett-)String in separate Strings, um pro Zeile ein Tabstop hinzufügen zu können
) -join $CrLf # Füge die Strings wieder zusammen
$Message+= $CrLf # Füge eine neue Zeile an
$Message+= '-' * 200 # Füge ein '-' als Erkennungs- und Trennzeichenmerkmal an
}
}
End {
$Message # String Ausgabe
}
}
########################################################################################
#Test Function
Function Get-MyItem {
[CmdletBinding()] # Wichtig, damit die -ErrorAction verfügbar werden in eigenen Funktionen; Bei Built-Ins bereits vorhanden
Param (
[string] $Path
)
Process {
Get-Item $Path
}
}
#Test Execution
Try {
Get-MyItem 'C:\System Volume Information' -ErrorAction Stop # Ohne "-ErrorAction Stop" wird der detaillierte Error nicht reportet
# Anderes Beispiel:
# Get-CimInstance Win342_tegsgf -ErrorAction stop
} Catch {
write-host(Out-Error $_) -ForegroundColor Red
}
Error-Output:
Man beachte insbesondere den ScriptStackTrace, welche auf die Zeile des Funktions-Aufrufes im Script (58), als auch auf die verursachende Zeile in der Funktion (52) aufmerksam macht!
Code:
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Could not find item C:\System Volume Information.
CategoryInfo : Category : ObjectNotFound
Activity : Get-Item
Reason : IOException
TargetName : C:\System Volume Information
TargetType : String
ErrorDetails :
Exception : Message : Could not find item C:\System Volume Information.
Data : {}
InnerException :
TargetSite :
StackTrace :
HelpLink :
Source :
HResult : -2146232800
FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetItemCommand
InvocationInfo : MyCommand : Get-Item
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 52
OffsetInLine : 3
HistoryId : 34
ScriptName :
Line : Get-Item $Path
PositionMessage : At line:52 char:3
+ Get-Item $Path
+ ~~~~~~~~~~~~~~
PSScriptRoot :
PSCommandPath :
InvocationName : Get-Item
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
PipelineIterationInfo :
PSMessageDetails :
ScriptStackTrace : at Get-MyItem<Process>, <No file>: line 52
at <ScriptBlock>, <No file>: line 58
TargetObject : C:\System Volume Information
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ergänzung ()
Anders Error-Output Beispiel mit WinSCP .NET Assembly:
Code:
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Exception calling "Open" with "1" argument(s): "Host "ftp.xxxxx.com" does not exist."
CategoryInfo : Category : NotSpecified
Activity :
Reason : MethodInvocationException
TargetName :
TargetType :
ErrorDetails :
Exception : ErrorRecord : Exception calling "Open" with "1" argument(s): "Host "ftp.xxxxx.com" does not exist."
WasThrownFromThrowStatement : False
Message : Exception calling "Open" with "1" argument(s): "Host "ftp.xxxxx.com" does not exist."
Data : {System.Management.Automation.Interpreter.InterpretedFrameInfo}
InnerException : WinSCP.SessionRemoteException: Host "ftp.xxxxx.com" does not exist.
at WinSCP.SessionLogReader.Read(LogReadFlags flags)
at WinSCP.ElementLogReader.Read(LogReadFlags flags)
at WinSCP.SessionElementLogReader.Read(LogReadFlags flags)
at WinSCP.CustomLogReader.TryWaitForNonEmptyElement(String localName, LogReadFlags flags)
at WinSCP.CustomLogReader.WaitForNonEmptyElement(String localName, LogReadFlags flags)
at WinSCP.Session.Open(SessionOptions sessionOptions)
at CallSite.Target(Closure , CallSite , Object , Object )
TargetSite : Void CheckActionPreference(System.Management.Automation.Language.FunctionContext, System.Exception)
StackTrace : at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
HelpLink :
Source : System.Management.Automation
HResult : -2146233087
FullyQualifiedErrorId : SessionRemoteException
InvocationInfo : MyCommand :
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 76
OffsetInLine : 2
HistoryId : -1
ScriptName : D:\XXXXX\WinSCP-5.11.2-Automation\Test.ps1
Line : $session.Open($sessionOptions)
PositionMessage : At D:\XXXXX\WinSCP-5.11.2-Automation\Test.ps1:76 char:2
+ $session.Open($sessionOptions)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSScriptRoot : D:\XXXXX\WinSCP-5.11.2-Automation
PSCommandPath : D:\XXXXX\WinSCP-5.11.2-Automation\Test.ps1
InvocationName :
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
PipelineIterationInfo :
PSMessageDetails :
ScriptStackTrace : at <ScriptBlock>, D:\XXXXX\WinSCP-5.11.2-Automation\Test.ps11: line 76
TargetObject :
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Zuletzt bearbeitet: