Search code examples
javaexceptiongroovycallstackbacktrace

In Groovy(/Java) how would I remove the current function from the stackTrace when throwing an Exception?


I'd like to provide a function for throwing exceptions rather than throwing them directly. E.g. instead of writing:

def foo() {
    def result = command_result("foo");
    if (!result) {
        throw new Exception("Bla!");
    }
    return result;
}

I'd like to write this:

def foo() {
    return command_result("foo") ?: error("Bla!");
}

which requires a function error() to be defined:

def error(msg) {
    throw new Exception(msg);
}

But of course I now have error() on top of the call stack, so if I wrote

try {
    print(foo());
} catch(Exception error) {
    print("ERROR: ${error.stackTrace.head()}: ${error}");
}

I'd get something like

ERROR: Script1.error(Script1.groovy:19): java.lang.Exception: Bla!

To get rid of error:19 on the stack I could just extract the second item on error.stackTrace I guess but that would force me to have one generic try/catch block.

Is it possible to provide such a functionality in Groovy (I guess it would be the same in Java) i.e. (re) throwing an Exception with the current function removed from the back trace?


Solution

  • Use getStackTrace, then drop items you don't want to be there, then use setStracTrace to change it in exception.

    https://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#setStackTrace(java.lang.StackTraceElement[])

    in groovy it's quite hard to drop all stacktrace elements that belong to latest call. however you could sanitize stacktrace (remove all groovy internal items) before doing this:

    def error(msg) {
      def e = new Exception(msg)
      e = org.codehaus.groovy.runtime.StackTraceUtils.sanitize(e)
      //drop items with unknown source and drop first element of stacktrace
      e.setStackTrace( e.getStackTrace().findAll{ it.getFileName() }.drop(1) as StackTraceElement[] )
      throw e
    }
    
    def foo(x) {
        return x ?: error("Bla!")
    }
    
    try{
      foo(0)
    }catch(e){
      assert e.stackTrace[0].methodName=='foo'
      e.printStackTrace()
    }
    

    Note: if you running this code in groovyconsole the View / Show Full Stack Trace menu item must be disabled to make sanitize working