Log Friendly Exception Handling

Normally for a typical junior, "in a rush", programmer, exception handling is something that is quickly solved by suppressing the exception, or catching it just to simply log the entire stack trace.

I can say that because I used to do it, not to care too much about the correct treatment of exceptions. Sometimes we tend to think that the most considerable approach is to log the exception and throw it up repeating the process in the subsequent classes.

  1. public class A {
  2. public void callB() throws {
  3. try {
  4. new B().doSomething();
  5. } catch ( exception) {
  6. exception.printStackTrace();
  7. throw ;
  8. }
  9. }
  10. }
  11.  
  12. public class B{
  13. public void doSomething() throws {
  14. try {
  15. // Code potentially exception throwing.
  16. } catch ( exception) {
  17. exception.printStackTrace();
  18. throw ;
  19. }
  20. }
  21. }

This is a common practice that looks harmless during development time but it isn't until trouble shooting the application in production environment, that we find that this is not a good idea. When we have to investigate an application issue, involving diving in the logs, the multiple appearances of one exception in various classes produces a big confusion for someone who has to interpret the record of all these exceptions.

This can also add noise to any monitor activity of the logs. The exception is multiplied by the number of classes that repeat the logging without adding any relevant information.The multiplication of these exceptions creates a perception that the problem seem in the logs is worse than it really is. This difficults the task to lead the efforts to the real problem.

Here we can have two choices to fix this according to the needs of the application:

1. Just log in the application's layer where the exception is produced and throw a RuntimeException. A RuntimeException does not to be catched.

  1. public class B{
  2. public void doSomething() throws {
  3. try {
  4. // Code potentially exception throwing.
  5. } catch ( exception) {
  6. exception.printStackTrace();
  7. throw new (exception);
  8. }
  9. }
  10. }

2. Another idea in case upper layers need to be aware of the exception, is to create customs exceptions (also known as wrappers) according to a set of categories in order to facilitate reading the logs. In the next code I show one example with two custom exceptions: one when the blame is on the user of the class for a bad setting of the configuration and the other one
when there is a remote access error.

  1. public class extends {
  2.  
  3. }
  4.  
  5. public class RemoteServiceException extends {
  6.  
  7. }
  8.  
  9.  
  10. public class B{
  11. public void doSomething( mathFormula) {
  12. try {
  13. // Code to execute formula.
  14. } catch ( exception) {
  15. throw new (exception);
  16. }
  17. }
  18.  
  19. public void doSomethingRemotely() {
  20. try {
  21. // Code to read Web Service.
  22. } catch ( exception) {
  23. throw new RemoteServiceException(exception);
  24. }
  25. }
  26. }
  27.  
  28. public class A {
  29. public void callB() {
  30. try {
  31. B b = new B();
  32. b.doSomething("filePath");
  33. b.doSomethingRemotely();
  34. } catch ( configurationException) {
  35. .out.println("There was a configuration error!");
  36. exception.printStackTrace();
  37. } catch (RemoteServiceExceptionremote) {
  38. .out.println("There is no connectivity with remote services!");
  39. exception.printStackTrace();
  40. }
  41. }
  42. }

Adding another level of abstraction helps in application's CSI activities, specially when the people in charge of these are not the same ones from the development team.