Hide the exception stacktrace from logs depending on log level - java

Let's say we have 2 projects: project A and B
Project A
This project defines some common exceptions. Doesn't have the slf4j dependency.
An example of Exception
public abstract class SomeException extends RuntimeException {
public SomeException (String msg) {
super(msg);
}
// just added, being able to hide the stacktrace, but it contains an additional param
public SomeException (String msg, boolean suppressStacktrace) {
super(msg, null, suppressStacktrace, !suppressStacktrace);
}
}
Project B
This is the main project. It includes the Project A dependency, and it calls the defined
SomeException in a lot of places. This project includes the slf4j dependency, and contains the slf4j config where we specify the logging level. I want to display the stacktrace just if the log level is DEBUG.
The challenge
I have a lot of usages of throw new SomeException(String msg), and I would like to hide the stacktrace in the Project A, based on the log level of the parent project Project B without changing the exception signature, as I will have to change it in 100 places. I am not able to have 2 super calls in a if/else statement. So the final change would look like:
public SomeException (String msg) {
// how to get the surpressStackTrace param from the parent project config?
this(msg, surpressStackTrace);
}
If this approach is not right, what other possibilities I have to hide the stacktrace without changing the called exception signature?
Note: these exceptions are non blocking. Even if the exception occurs, the flow is not interrupted. Can I use #ControllerAdvice so that after the exception is handled, the flow is continued?
I also tried this:
#ControllerAdvice
public class ControllerAdvisor {
private static final Logger logger = LogManager.getLogger(ControllerAdvisor.class);
#ExceptionHandler(SomeException .class)
public void handleSomeException(SomeException ex) {
logger.log(Level.INFO, ex.getMessage());
if(Level.DEBUG.equals(logger.getLevel())){
logger.log(Level.DEBUG, ex.getStackTrace());
}
}
}
However, this terminates the execution of the further logic. Is it possible to continue the flow after the exception is handled?

Not sure if I fully understand your question...
But here is a possible solution:
Ignores SomeException (you can because it is a RuntimeException)
Keeps the normal execution process.
Logs when the exception was thrown, with stack trace when log-level is DEBUG and without when the log-level is not DEBUG
This could be done with an Aspect
Make sure you have spring-aop on your classpath; for spring-boot, you can include spring-boot-starter-aop as dependency.
Enable AOP by adding #EnableAspectJAutoProxy to one of your configuration classes.
Write a #Around-advise to intercept all the methods where the exception can occur. Do a try-catch, but proceed with the normal execution of the code when a SomeException is thrown.
Log the message with or without the stack trace based on the log level.
This could look something like this:
package com.example.demo.aop;
import com.example.demo.exception.SomeException;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.slf4j.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class DemoAspect {
private static final Logger logger = LoggerFactory.getLogger(DemoAspect.class);
// all executed public method-calls in the package com.example.demo
#Pointcut(value = "execution(public * com.example.demo..*(..))")
private void pointcut(){
}
// intercept the defined pointcut
#Around("pointcut()")
public Object ignoreSomeException(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
try {
result = pjp.proceed(); // <-- try normal execution
} catch (SomeException e) {
log(e);
result = pjp.proceed(); // <-- continue normal execution
} finally {
return result;
}
}
// Log the exception based on the loglevel
private void log(Throwable e) {
if(logger.isDebugEnabled()) {
logger.error(e.getMessage(), e); // <-- log with stacktrace
} else {
logger.error(e.getMessage()); // <-- only log the message
}
}
}

Related

Log exceptions and continue execution without clutter on code

I am looking into implementing a standard error handling on my application. I want errors that cant be dealt with(custom unchecked errors) but that also aren't catastrophic to be logged by a fault barrier without having to clutter my code with pesky try catches and logger calls.
Let me illustrate.
I have an intake that receives a json string and set it into my object model, one of the fields in said model call a timeHelper function, this function can throw an exception if the arguments are invalid(null or empty). The yield of this function is not critical to the program, in fact this program should never crash( to the best of my abilities) as it should stay up 24/7.
Model
public class MyModel{
private string myField
public void setMyField(String myfield){
this.myField = Helper.DoStuff(myField)
}
}
Intake
public class Intake{
public MyModel receiveJson(){
return JacksonMagic(arguments,MyModel.class)
}
}
Helper
Public class Helper{
public String DoStuff(String myField){
//Check that can throw exception
//regular operation with return
}
}
Now, when life is beautiful DoStuff returns a string, in fact the exception should never be thrown because it implies that the source of the json, which is external to my application, sent wrong/missing information. If it does happen I want it to be logged so I can investigate what happened. I also want to set a framework in place, probably with Spring AOP, to handle that logging. But as you can see through the example, I also want execution to continue as this is not some app breaking thing.
The execution flow I am looking for is something like
Intake > Model > Helper(THROW EXCEPTION) > Logger > Whoever Called Intake
And again, I want to do that without the try catch logger call cluter
Is this something possible with AOP?
Post answer Edit
Just want to leave some sources here.
To set up your IDE for AspectJ compilation, this article is really helpful.
https://www.baeldung.com/aspectj
This is not a good use case for exceptions.
An exception represents something that you're not able to handle, an "exceptional" occurrence that you're not able to deal with. The fact that you're saying this is a possible scenario changes this from an exception to a use-case, in which case logging a warning in your service tier is probably the best solution.
Exceptions have their place, however overusing them makes code harder to follow, since it breaks the "flow" of an application. Exceptions should not be used to control flow.
AOP, in my option, offers little when it comes to exception handling. At best it can log the exception (which can also be achieved in a much clearer way using an ExceptionHandler pattern), however it certainly can't trigger your code to continue as though it didn't happen.
If you haven't already, look into logging strategies, they can be really useful in this kind of scenario.
The bottom line is: if you want control flow to continue, don't throw an exception (checked or unchecked).
Okay, here we go with a complete MCVE, assuming you know how to use the AspectJ compiler in order to compile your project. Sorry for repeating your classes with package names, imports etc., but I like you to see all details:
First we need our helper which randomly throws unchecked exceptions so we can see the aspect in action later:
package de.scrum_master.app;
import java.util.Random;
public class Helper {
private static final Random RANDOM = new Random();
public static String doStuff(String myField) {
if (RANDOM.nextBoolean())
throw new RuntimeException("uh-oh!");
return "processed " + myField;
}
}
package de.scrum_master.app;
public class MyModel {
private String myField;
public void setMyField(String myField) {
this.myField = Helper.doStuff(myField);
}
#Override
public String toString() {
return "MyModel(myField=" + myField + ")";
}
}
package de.scrum_master.app;
public class Intake {
public MyModel receiveJson(String... arguments) {
return jacksonMagic(arguments, MyModel.class);
}
public MyModel jacksonMagic(String[] arguments, Class<?> clazz) {
MyModel myModel = new MyModel();
myModel.setMyField(arguments[0]);
return myModel;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
System.out.println(new Intake().receiveJson("foo"));
}
}
Now when you run the little driver application via Intake.main you will see unhandled exceptions on the console. Here is how to handle this using an aspect. I am limiting the aspect to matching all method executions with a String return type, stupidly returning a dummy value whenever an exception occurs. You just add your more sophisticated logic in there as you see fit and also adjust the aspect's pointcut to match the methods you want to handle.
package de.scrum_master.aspect;
public aspect ErrorHandler {
String around() : execution(String *(..)) {
try {
return proceed();
} catch (Exception e) {
System.out.println("Exception handled: " + e);
return "dummy";
}
}
}
I love the expressive native AspectJ syntax, but I know that some people for whatever reason feel more comfortable with annotation-based syntax. Just look at the throws declaration, the pointcuts in string constants, the explicit joinpoint declaration, the cast - yuck! Anyway, here we go:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class ErrorHandler {
#Around("execution(String *(..))")
public String handleErrors(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return (String) thisJoinPoint.proceed();
} catch (Exception e) {
System.out.println("Exception handled: " + e);
return "dummy";
}
}
}
The console log looks like this with the aspect in place:
MyModel(myField=processed foo)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)

Java - listen when an exception is caught in an external class

Is there a way to create a listener in a separate class that runs a certain piece of code whenever an exception is caught within your project?
My code has a lot of try-catches in it, and if an exception is caught I would like to see the logs using log4j. I can do the following for every try-catch I have fairly easily (just with some time effort):
private static final Logger logger = LogManager.getLogger(Example.class);
public void testMethod() {
try {
// some code here that could throw an exception
} catch(Exception e) {
logger.error("Unexpected error has occurred: ", e);
}
}
This will log the exception using log4j. However, I would need to do that over 50 times, and it's so redundant that I would rather be able to use 1 method to do that. So, is there a way to instead do something like this?
public class ListenerClass {
private static final Logger logger = LogManager.getLogger(ListenerClass.class);
// This method will be listening for exceptions to be caught within the project
/**
* #param e - The exception that was just caught
*/
public void listenerMethod(ExceptionCaught e) {
logger.error("An exception has been thrown: ", e);
}
}
Is this possible?
Thanks
Standard java way:
Thread.setDefaultUncaughtExceptionHandler( (thread, throwable) -> {
log(throwable.getMessage(), thread.getId());
});
which will handle uncaught RuntimeExceptions, and unless otherwise specified it will act for all your application threads.
Just remember the Exceptions are thrown for a reason, and shouldn't be ignored, especially RuntimeExceptions.
If you are using an older version of java (before 8), you must explicitly instantiate an anonymous class:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(final Thread t, final Throwable e) {
}
});
Look at Thread.setDefaultUncaughtExceptionHandler()

Rescuing a swallowed Exception in Java

Some 3rd party library swallowed an Exception:
String getAnswer(){
try{
// do stuff, modify instance state, maybe throw some exceptions
// ...
return computeAnswer();
}catch (SomeException e){
return null;
}
}
As much as I want to change it into:
String getAnswer() throws SomeException{
// do stuff, modify instance state, maybe throw some exceptions
// ...
return computeAnswer();
}
I can't, because the library is already packaged into a jar. So, is there a way to bring the exception back?
I don't need to rethrow, a stacktrace with exception and message would work too.
I don't think reflection would help here, Unsafe perhaps?
Yes I know I can use a debugger to find out what's happening, but that wouldn't be very useful if I need the exception at runtime for logging and stuff like that
You can do it without reflection or AOP. The main idea is to throw another (unchecked) exception in the constructor of SomeException. There are some limitations (see at the end of this answer) but I hope it fits your needs.
You need to replace the SomeException with a new version (just create a SomeException.java file in the original package but in your src directory) with something like :
package com.3rdpartylibrary;
public class SomeException extends Exception {
public static class SomeExceptionWrapperException extends RuntimeException {
public SomeExceptionWrapperException(final SomeException ex) {
super(ex.getMessage(), ex);
}
}
public SomeException(final String message) {
super(message);
throw new SomeExceptionWrapperException(this); //<=== the key is here
}
}
The SomeExceptionWrapperException has to be unchecked (inherit from RuntimeException or Error). It will be our wrapper to carry the SomeException accross the ugly 3rd party catch(...)
Then you can catch the SomeExceptionWrapperException in your code (and eventually rethrow the original SomeException:
//original, unmodifiable 3rdParty code, here as a example
public String getAnswer() {
try {
//some code
throw new SomeException("a message");
} catch (final SomeException e) {
return null;
}
}
//a wrapper to getAnswer to unwrapp the `SomeException`
public String getAnswerWrapped() throws SomeException {
try {
return getAnswer();
} catch (final SomeExceptionWrapperException e) {
throw (SomeException) e.getCause();
}
}
#Test(expected = SomeException.class)
public void testThrow() throws SomeException {
final String t = getAnswerWrapped();
}
The test will be green as the original SomeException, will be thrown.
Limitations:
This solution will not work if either :
if SomeException is in java.lang as you cannot replace java.lang classes (or see Replacing java class?)
if the 3rd party method has a catch(Throwable e) (which will be horrible and should motivate you to ignore the full 3rd party library)
To solve this based on your constraints I would use aspects (something like AspectJ) and attach it to the creation of your exception, logging (or having it call some arbitrary) method then.
http://www.ibm.com/developerworks/library/j-aspectj/
If all you're looking for is to log the stacktrace + exception message, you could do that at the point you're throwing your exception.
See Get current stack trace in Java to get the stack trace. You can simply use Throwable.getMessage() to get the message and write it out.
But if you need the actual Exception within your code, you could try and add the exception into a ThreadLocal.
To do this, you would need a class like this that can store the exception:
package threadLocalExample;
public class ExceptionKeeper
{
private static ThreadLocal<Exception> threadLocalKeeper = new ThreadLocal<Exception>();
public static Exception getException()
{
return threadLocalKeeper.get();
}
public static void setException(Exception e)
{
threadLocalKeeper.set(e);
}
public static void clearException()
{
threadLocalKeeper.set(null);
}
}
... then in your code which throws the Exception, the code that the 3rd party library calls, you can do something like this to record the exception before you throw it:
package threadLocalExample;
public class ExceptionThrower
{
public ExceptionThrower()
{
super();
}
public void doSomethingInYourCode() throws SomeException
{
boolean someBadThing = true;
if (someBadThing)
{
// this is bad, need to throw an exception!
SomeException e = new SomeException("Message Text");
// but first, store it in a ThreadLocal because that 3rd party
// library I use eats it
ExceptionKeeper.setException(e);
// Throw the exception anyway - hopefully the library will be fixed
throw e;
}
}
}
... then in your overall code, the one that calls the third party library, it can setup and use the ThreadLocal class like this:
package threadLocalExample;
import thirdpartylibrary.ExceptionEater;
public class MainPartOfTheProgram
{
public static void main(String[] args)
{
// call the 3rd party library function that eats exceptions
// but first, prepare the exception keeper - clear out any data it may have
// (may not need to, but good measure)
ExceptionKeeper.clearException();
try
{
// now call the exception eater. It will eat the exception, but the ExceptionKeeper
// will have it
ExceptionEater exEater = new ExceptionEater();
exEater.callSomeThirdPartyLibraryFunction();
// check the ExceptionKeeper for the exception
Exception ex = ExceptionKeeper.getException();
if (ex != null)
{
System.out.println("Aha! The library ate my exception, but I found it");
}
}
finally
{
// Wipe out any data in the ExceptionKeeper. ThreadLocals are real good
// ways of creating memory leaks, and you would want to start from scratch
// next time anyway.
ExceptionKeeper.clearException();
}
}
}
Beware of ThreadLocals. They have their use, but they are a great way of creating memory leaks. So if your application has a lot of threads that would execute this code, be sure to look at the memory footprint and make sure the ThreadLocals aren't taking up too much memory. Being sure to clear out the ThreadLocal's data when you know you no longer need it should prevent that.
JVMTI agent can help. See the related question.
I've made an agent that calls Throwable.printStackTrace() for every thrown exception, but you may easily change the callback to invoke any other Java method.
A rather dirty trick that could do the job with less effort than AOP or de-/recompile the JAR:
If you can copy the source code, you can create a patched version of the class in question with your version of the getAnswer method. Then put it on your classpath before the third party library that contains the unwanted version of getAnswer.
Problems could arise if SomeException is not a RuntimeException and other third party code calls getAnswer. In this situation I am not sure how the resulting behavior will be. But you could circumvent this by wrapping SomeException in a custom RuntimeException.
Could you not just use a reference variable to call that method, if the result is a null, then you can just display a message/call an exception, whatever you want?
if you're using maven, you would exclude packages of the library.
Dependency Exclusions.
I hope to be helpful
If you have the source to the throwing class, you can add it "in the original package but in your src directory" using the technique as #BenoƮt has pointed out. Then just change
return null;
to
return e;
or
e.printStackTrace();
etc.
This would be quicker then making a new Exception.

Junit test to fail without failure trace

I'm running JUnit tests from Eclipse, and using the command Assert.fail(message) whenever I want to cause the test to fail. The problem is that I always get the message along with the entire failure trace. Is there any way for JUnit to display the message only, without the stack trace?
Eclipse JUnit settings available via Window --> Preferences --> Java --> Junit allow for cutting stack traces for certain exception classes:
With these settings, the stack trace for the test:
#Test
public void test() {
fail("Not yet implemented");
}
is filtered:
Now try to deselect the "org.junit.*" (or "junit.framework.Assert", depending which version of JUnit you're using), and rerun the test. The stack trace immediately becomes scary:
Perhaps this is the feature you're looking for.
If a test throws an error, the test will normally fail automatically without a message. You could of course catch Exceptions and let the test fail in the catch clause. Something like this:
#Test
public void test() {
try {
// your test
} catch (Exception e) {
fail("I caught an " + e.class.getSimpleName());
}
}
Of course as always when dealing with exceptions, be as precise when catching them as possible.
I found a way to crash the test without showing a long failure trace. Instead of using Assert.fail(), I create my own exception, and set the failure trace to be minimal:
public static void failTest(String message) throws MyTestFailure {
System.err.println(message);
MyTestFailure exception = new MyTestFailure(message);
StackTraceElement elem = new StackTraceElement("com.my.package.Utils", "failTest", "failTest", 3);
StackTraceElement elem1[] = new StackTraceElement[1];
elem1[0] = elem;
exception.setStackTrace(elem1);
throw exception;
}
and this way I only print the message that I want to print, and a single line after that
I would simply override the printStackTrace()-methods for my custom Throwable:
Exception -> ERROR
AssertionError -> FAILURE
import java.io.PrintStream;
import java.io.PrintWriter;
public class TestErrorException extends Exception{
private static final long serialVersionUID = -3486394019411535690L;
private String message;
public TestErrorException(String message) {
this.message = message;
}
#Override
public void printStackTrace() {
System.err.println(message);
}
#Override
public void printStackTrace(PrintStream s) {
s.println(message);
}
#Override
public void printStackTrace(PrintWriter s) {
s.println(message);
}
}

Wrap exceptions by runtime exceptions with an annotation

Is there a way to annotate a method so all exceptions thrown are converted to runtime exception automagically?
#MagicAnnotation
// no throws clause!
void foo()
{
throw new Exception("bar")'
}
Project Lombok's #SneakyThrows is probably what you are looking for. Is not really wrapping your exception (because it can be a problem in a lot of cases), it just doesn't throw an error during compilation.
#SneakyThrows
void foo() {
throw new Exception("bar")'
}
You can do this with AspectJ. You declare a joinpoint (in this case invocation of the method foo) and 'soften' the exception.
Edit To elaborate a bit on this:
Say you have the following class Bar:
public class Bar {
public void foo() throws Exception {
}
}
...and you have a test like this:
import junit.framework.TestCase;
public class BarTest extends TestCase {
public void testTestFoo() {
new Bar().foo();
}
}
Then obviously the test is not going to compile. It will give an error:
Unhandled exception type Exception BarTest.java(line 6)
Now to overcome this with AspectJ, you write a very simple aspect:
public aspect SoftenExceptionsInTestCode {
pointcut inTestCode() : execution(void *Test.test*());
declare soft : Exception : inTestCode();
}
The aspect basically says that any code from within a Test (i.e.: a method that starts with "test" in a class that ends in "Test" and returns 'void') that throws an exception should be accepted by the AspectJ compiler. If an exception occurs, it will be wrapped and thrown as a RuntimeException by the AspectJ compiler.
Indeed, if you run this test as part of an AspectJ project from within Eclipse (with AJDT installed) then the test will succeed, whereas without the aspect it won't even compile.
No way to do that, at least for now I use workaround like this (simplified):
#SuppressWarnings({"rawtypes", "unchecked"})
public class Unchecked {
public static interface UncheckedDefinitions{
InputStream openStream();
String readLine();
...
}
private static Class proxyClass = Proxy.getProxyClass(Unchecked.class.getClassLoader(), UncheckedDefinitions.class);
public static UncheckedDefinitions unchecked(final Object target){
try{
return (UncheckedDefinitions) proxyClass.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target instanceof Class){
return MethodUtils.invokeExactStaticMethod((Class) target, method.getName(), args);
}
return MethodUtils.invokeExactMethod(target, method.getName(), args);
}
});
}
catch(Exception e){
throw new RuntimeException(e);
}
}
}
And the usage looks like:
import static ....Unchecked.*;
...
Writer w = ...;
unchecked(w).write(str, off, len);
The trick is that interface is "never finished" and everytime I need unchecked method somewhere, I'll wrap that object into unchecked and let IDE generate method signature in interface.
Implementation is then generic (reflective and "slow" but usually fast enough)
There are some code post-processors and bytecode-weavers but this was not possible (not even aop or other jvm based language) for my current project, so this was "invented".
I think it is possible with bytecode re-engineering, customized compiler or perhaps aspect oriented programming1. In the contrary to Java, C# has only unchecked exceptions2.
May I ask why you want to suppress the checked exceptions?
1 according to Maarten Winkels this is possible.
2 and they are thinking about introducing checked ones, according to some Channel 9 videos.
Edit: For the question: It is possible in the sense that you can annotate your methods to flag them to be a candidate for checked exception suppression. Then you use some compile time or runtime trick to apply the actual suppression / wrapping.
However, as I don't see the environment around your case, wrapping an exception in these ways might confuse the clients of that method - they might not be prepared to deal with a RuntimeException. For example: the method throws an IOException and your clients catches it as FileNotFoundException to display an error dialog. However if you wrap your exception into a RuntimeException, the error dialog gets never shown and probably it kills the caller thread too. (IMHO).
The Checked exceptions are responsability of the method implementation.
Take very very carefully this fact. if you can do not use workaround artifacts like that.
You can do this in any case via use of the fact that Class.newInstance does not wrap an Exception thrown by the no-arg constructor in an InvocationTargetException; rather it throws it silently:
class ExUtil {
public static void throwSilent(Exception e) { //NOTICE NO THROWS CLAUSE
tl.set(e);
SilentThrower.class.newInstance(); //throws silently
}
private static ThreadLocal<Exception> tl = new ThreadLocal<Exception>();
private static class SilentThrower {
SilentThrower() throws Exception {
Exception e = tl.get();
tl.remove();
throw e;
}
}
}
Then you can use this utility anywhere:
ExUtil.throwSilent(new Exception());
//or
try {
ioMethod();
} catch (IOException e) { ExUtil.throwSilent(e); }
By the way, this is a really bad idea :-)
I use the completion / template system of Eclipse to wrap any block of code easily.
Here is my template :
try { // Wrapp exceptions
${line_selection}${cursor}
} catch (RuntimeException e) { // Forward runtime exception
throw e;
} catch (Exception e) { // Wrap into runtime exception
throw new RuntimeException(
"Exception wrapped in #${enclosing_method}",
e);
}

Categories