PowerShell - How to log FULL exception including all useful details? - powershell

It has been a while since the last time I wrote a powershell script. The first thing I did is implement a logging function into my script but now I noticed that no matter if I log $_ or $_.Exception in a catch-block, there is always some information missing when the log is written to a file. How can I log the exception with all details like line number, stack trace and so on to a file without writing like 5 lines of code just for formatting the exception manually? Is there really no standard way of doing this in powershell?

Technically it depends on exception.
Every exception is an instance of Exception class or one of derivatives.
This means that all Exceptions guaranteed to have basic properties and methods listed in MSDN Exception doc, but additionally Exceptions can have it's own properties or methods. For example, ArgumentException is derivative from Exception, but is has an optional ParamName property.
You can catch predicted exceptions using different catchers or you can get all properties using, for example, Get-Member, and log them. But catch block should not be complex.
Additionally, try using Start-Transcript instead of custom logging. Maybe this will be enough for your needs. Don't forget to set compression flag on folder where transcriptions will be written to.

Related

PowerShell log all unhandled exceptions

I am looking to log unhandled exceptions, since my script runs as an automated tool with no one monitoring progress at the console. My thinking is to create my own exception class, which has a constructor that accepts [InvokationInfo] so I can then log the error with file and line number info, and possibly the trace stack as well. The goal is to catch as many exception types as possible and handle them, but have a generic final catch for exceptions that are literally my code failing. It occurs to me that the entire script would be one big try/catch so that literally anything I didn't expect (so long as it's terminating) would get caught and logged.
To that end I have this mocked up:
class UnhandledException : Exception {
UnhandledException () : base () {
}
UnhandledException ([System.Management.Automation.InvocationInfo] $InvokationInfo) {
Write-Host "Unhandled Exception in $([System.IO.Path]::GetFileName($InvokationInfo.ScriptName)) at line $($InvokationInfo.ScriptLineNumber)"
}
}
CLS
try {
1/0
} catch [System.IO.IOException] {
# Handled exception types go here
} catch {
$unhandledException = [UnhandledException]::new($PSItem.InvocationInfo)
#throw $unhandledException
}
This seems to be working well, and I am debating if that final throw is needed, or if I can just as well terminate the script from within the exception, since by definition once I have logged that info, and maybe thrown up toast message about the failure, I will be exiting the script anyway.
My question is, is this an appropriate way to handle exceptions when the script is functioning as a silent command line utility? I really don't want to have a situation where, if the console is showing, that powershell exceptions are visible. I want to handle everything, to the extent I can, quietly and with log files. Those logs could then be sent to me so I could troubleshoot.
That said, I have not found any info on wrapping the entire script in a try/catch. This
suggests that "catch everything" is a code smell, but it's also talking more about methods that are consumed by other users, not utility scripts.
The bit about std::uncaught_exception() sounds like it might be an option too, if I could have my regular log, for logging actual progress of the script and error in data, inability to access network resources, etc. All of which would be exceptions that I do catch and handle. If I could define a log file that is ONLY for otherwise uncaught exceptions that might be even better, but I also haven't found anything like that for PowerShell. So my approach is my backup plan. Unless I am missing something and this is a horrible idea?
And, to be clear, I am not thinking this would be the ONLY exception handling. This would be the handler of last resort, logging anything that I hadn't otherwise expected, planned for and handled.
EDIT: So, a limitation I have found to trap is that it still only traps terminating errors, and ideally I would like to also get a log of continued errors as well. To that end, I have been exploring redirects I have tried this
function LocalFunction {
1/0
}
&{
CLS
LocalFunction
Remove-Item 'Z:\no.txt' -errorAction silentlyContinue
Test-Path 'C:\'
Write-Host 'Continued'
} 2>> c:\errors.txt
This will successfully log the divide by 0 error in the function and the error at Remove-Item when -errorAction is Continue (the default), but when I specially set it to SilentlyContinue it isn't logged. This seems to get me where I want to be, with ANY error that would be seen in the console instead going to the text file. I could then, at the end of processing, test the size of that file, and delete if 0 or provide a toast message if something got logged.
The real question becomes, is the &{} construct around basically the entire script a viable option once it's a 10,000 line script, rather than a little example? And is it a good idea in general? Or is this perhaps something useful during development, but I just need to put on my big boy pants and actually HANDLE every possible error?
EDIT 2: Well, after doing some tests on a branch of my utility, this redirect approach is actually looking REALLY promising. Apparently no impact on performance, and I can even add the contents of my errors log to my regular log to make things easier for users. Curious if anyone has some counter indications?
Also, a little digging suggest that Invoke-Expression might be better, because the & operator creates a child scope, and that might cause problems while Invoke-Expression doesn't. But on the other hand Invoke-Expression is right up there with Regular Expressions in the "Don't do that" hierarchy. Things that make you go hmmmmmm?

Can a matlab function called within a script cause the script to break?

I am running a script which calls a function, and if a certain condition is met, inside the function, I want the whole thing just to terminate (and by that I do not mean I want to close matlab using exit). Is that possible? I know I can use return or break to return to the script, however I want the script to stop as well if this condition is met.
The only function I know of that does this is error. This throws an exception, and, if no exception handlers with try and catch are installed in the calling script, will terminate and return to command prompt. Which is what you want, as far as I understand. It prints an error message though. This could be suppressed if you guard all code in the top-level script with a try catch handler. However this will have to be specific to the one error and it makes debugging ("stop-on-error") much more difficult.
The thing is that the only use case I see for this behavior (termination of whole program on certain event) is when a non recoverable error occurs, and in that case printing an error message is indeed appropriate.
In case the script is successful termination of the whole program is not really the right way. All functions should return to give the upper layers of the code to perform some clean-up action, like saving the output data or so.

Get name of matlab file one step up in stack

I'm writing a matlab script where I'd like to know the name of the script which is one step up in the stack. The reason I need this is that I'm writing a helper script to be called to assert some basic facts about the workspace before engaging in heavier calculations, and the helper script should be able to give error messages when calling the calculation scripts.
I've tried using mfilename and dbstack, but I do not manage to get anything but the name of the helper script from them.
To explain further, I have the following scripts:
verify_base_conditions: the helper script, called on one of the first lines in
heavy_stuff_calculation and
sweet_mother_of_algorithms.
general_entry_point: a script where I gather common workflows in cells
I try to keep the entry point script as small as possible, and refactor heavier calculations (or anything that needs more than a few lines, really) into task-based, re-usable scripts. If I, from the entry point script, call sweet_mother_of_algorithms and the basic conditions for this script to run are not fulfilled, I want to see an error message like
Error: this_cool_var must be defined when running sweet_mother_of_algorithms
general_entry_point line 15
I tried using
error(['this_cool_var must be defined when running ' mdfilename])
and other similar constructs, but I couldn't make the error message say anything but verify_base_conditions.
How do I find the name of the script running one level up in the stack?
(Note: I'm not interested in finding the script running at the bottom of the stack - that will almost always be general_entry_point. I'm just interested in getting the name of the script that called verify_base_conditions.)
dbstack should work for this. I think this is an analogous set-up to yours:
a.m:
b;
b.m:
c;
c.m:
stack = dbstack;
for i=1:3
disp(stack(i));
end
When I run a;, this prints out the whole stack, including b (which I think it was you want - the second-top item on the call stack.
Alternatively, although this probably gives exactly the same result, you could throw and catch an error and look at its stack:
c.m:
try
error('What is on the call stack?');
catch e
for i=1:3
disp(e.stack(i));
end
end

How to "throw" a %Status to %ETN?

Many of the Caché API methods return a %Status object which indicates if this is an error. The thing is, when it's an unknown error I don't know how to handle (like a network failure) what I really want to do is "throw" the error so my code stops what it's doing and the error gets caught by some higher level error handler (and/or the built-in %ETN error log).
I could use ztrap like:
s status = someObject.someMethod()
ztrap:$$$ISERR(status)
But that doesn't report much detail (unlike, say, .NET where I can throw an exception all the way to to the top of the stack) and I'm wondering if there are any better ways to do this.
Take a look at the Class Reference for %Exception.StatusException. You can create an exception from your status and throw it to whatever error trap is active at the time (so the flow of control would be the same as your ZTRAP example), like this
set sc = someobj.MethodReturningStatus()
if $$$ISERR(sc) {
set exception = ##class(%Exception.StatusException).CreateFromStatus(sc)
throw exception
}
However, in order to recover the exception information inside the error trap code that catches this exception, the error trap must have been established with try/catch. The older error handlers, $ztrap and $etrap, do not provide you with the exception object and you will only see that you have a <NOCATCH> error as the $ZERROR value. Even in that case, the flow of control will work as you want it to, but without try/catch, you would be no better off than you are with ZTRAP
These are two different error mechanisms and can't be combined in this way. ztrap and %ETN are for Cache level errors (the angle bracket errors like <UNDEFINED>). %Status objects are for application level errors (including errors that occurred through the use of the Cache Class Library) and you can choose how you want to handle them yourself. It's not really meaningful to handle a bad %Status through the Cache error mechanism because no Cache error has occurred.
Generally what most people do is something akin to:
d:$$$ISERR(status) $$$SomeMacroRelevantToMyAppThatWillHandleThisStatus(status)
It is possible to create your own domain with your own whole host of %Status codes with attendant %msg values for your application. Your app might have tried to connect to an FTP server and had a bad password, but that doesn't throw a <DISCONNECT> and there is no reason to investigate the stack, just an application level error that needs to be handled, possibly by asking the user to enter a new password.
It might seem odd that there are these two parallel error mechanisms, but they are describing two different types of errors. Think of one of them being "platform" level errors, and the other as "application level errors"
Edit: One thing I forgot, try DecomposeStatus^%apiOBJ(status) or ##class(%Status).LogicalToOdbc(status) to convert the status object to a human readable string. Also, if you're doing command line debugging or just want to print the readable form to the principal device, you can use $system.OBJ.DisplayError(status).

error handling vs exception handling in objective c

I am not able to understand the places where an error handling or where an exception handling should be used. I assume this, if it is an existing framework class there are delegate methods which will facilitate the programmer to send an error object reference and handle the error after that. Exception handling is for cases where an operation of a programmer using some framework classes throws an error and i cannot get an fix on the error object's reference.
Is this assumption valid ? or how should i understand them ?
You should use exceptions for errors that would never appear if the programmer would have checked the parameters to the method that throws the exception. E.g. divide by 0 or the well known "out of bounds"-exception you get from NSArrays.
NSErrors are for errors that the programmer could do nothing about. E.g. parsing a plist file. It would be a waste of resources if the program would check if the file is a valid plist before it tries to read its content. For the validity check the program must parse the whole file. And parsing a file to report that it is valid so you can parse it again would be a total waste. So the method returns a NSError (or just nil, which tells you that something went wrong) if the file can't be parsed.
The parsing for validity is the "programmer should have checked the parameters" part. It's not applicable for this type of errors, so you don't throw a exception.
In theory you could replace the out of bounds exception with a return nil. But this would lead to very bad programming.
Apple says:
Important: In many environments, use of exceptions is fairly commonplace. For example, you might throw an exception to signal that a routine could not execute normally—such as when a file is missing or data could not be parsed correctly. Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors. Instead you should use the return value of a method or function to indicate that an error has occurred, and provide information about the problem in an error object.
I think you are absolutely right with your assumption for Errors and for it framework provide a set of methods (UIWebView error handling ), But your assumption for Exception partially right because the exception only occurred if we do something wrong which is not allowed by the framework and can be fixed. (for example accessing a member from an array beyond its limit).
and will result in application crash.