Create new version of Eval/VoidEval that handles errors
Would be really nice to get the actual error that R throws instead of "R threw and error". To do that, we have to wrap all of the calls in a try(eval()) call.
see: http://www.rforge.net/Rserve/faq.html#errors
I have had good results with the alternate form of error handling - catch the RserveException and try to call geterrmessage() and traceback() for more details. geterrmessage() has been very reliable; traceback() not so much but occasionally I do get something. Here is an implementation as extension methods on RConnection - change the logging to suit:
/// Extension methods for Rserve components
public static class RserveExtensions
{
/// Eval method that catches errors and returns the actual R error.
/// See http://www.rforge.net/Rserve/faq.html#errors
/// According to the Rserve
/// docs the try() method is more reliable; however this method
/// sometimes gives us a stack trace which is very helpful.
public static Sexp TryEval(this RConnection conn, String expr)
{
try
{
return conn.Eval(expr);
}
catch (RserveException ex)
{
GetAndThrowRealError(conn, ex);
return null; // We won't get here but the compiler doesn't know that.
}
}
/// VoidEval method with error reporting.
public static void TryVoidEval(this RConnection conn, String expr)
{
try
{
conn.VoidEval(expr);
}
catch (RserveException ex)
{
GetAndThrowRealError(conn, ex);
}
}
/// Try to get a real error message and stack trace; if that fails rethrow the original exception.
private static void GetAndThrowRealError(RConnection conn, RserveException ex)
{
// Try to get the error message
String msg;
try
{
msg = conn.Eval("geterrmessage()").AsString;
}
catch
{
throw ex;
}
if (String.IsNullOrWhiteSpace(msg))
throw ex;
// Try to get the stack trace
// It's possible that geterrmessage() succeeds and traceback() fails.
// If so just use the error message
try
{
var tracebacks = conn.Eval("traceback()").AsStrings;
var traceback = String.Join("\r\n", tracebacks);
Log.TraceError("Rserve error: {0}Traceback: {1}", msg, traceback);
#if DEBUG
msg = msg + traceback;
#endif
}
catch
{
// Log msg only
Log.TraceError("Rserve error: {0}", msg);
}
// Throw with a helpful message
throw new RserveException(msg);
}
}
Rserve recommends the try(eval()) approach which I like because you don't have to go to the server to get the error, it comes back as part of the evaluation. Also, I think RserveException can be thrown for issues unrelated to the user computation (like network issues). So we wouldn't want to go back to the server in those cases. Let me think about this some more. It seems like you have something that works, so we should implement it. Will be in touch...
Yes, I will try two more round trips to the server; one for geterrmessage() and one for traceback(). My server is running on the same machine as the client so the round trips are fast and the error is generally going to end up displaying in a dialog box so speed is not critical.
One thing I like about my approach is I don't have to modify the client command.
I am finding that I rarely get tracebacks, not sure why that is.
Kent
On Wed, Jul 9, 2014 at 4:33 PM, Suraj Gupta [email protected] wrote:
Rserve recommends the try(eval()) approach which I like because you don't have to go to the server to get the error, it comes back as part of the evaluation. Also, I think RserveException can be thrown for issues unrelated to the user computation (like network issues). So we wouldn't want to go back to the server in those cases. Let me think about this some more. It seems like you have something that works, so we should implement it. Will be in touch...
— Reply to this email directly or view it on GitHub https://github.com/SurajGupta/RserveCLI2/issues/10#issuecomment-48530353 .