0

I'm building a multi-tier enterprise application using Spring. I have different layers: Controller, Business and Provider. Within the application I've built a custom error handling mini-framework that is based on a single RuntimeException which has an error code to discriminate different kind of errors.

Error codes are enums implementing this interface:

public interface ErrorCode {
    public int getNumber();
    public String getDeveloperMessage();
    public String getHelpURL();
    public boolean isSystemError();
}

So for example I have:

public enum SystemErrorCode implements ErrorCode{
    E_UNKNOWN_ERROR(10000, "Unkwown internal error", true),
    E_MISSING_ARGUMENT(10001, "Missing argument", true),
    E_INVALID_ARGUMENT(10002, "Invalid argument", true),
    E_CONTEXT_NO_TENANT(10003, "No tenant in context", true),
    E_CONTEXT_TENANT_CHANGE(10004, "Tenant change attempt", true),
    E_CONTEXT_ALREADY_INITIALIZED(10005, "Tenant already initialized", true),
    E_NOT_IMPLEMENTED(10005, "Feature not implemented", true),
    ...
}

The exception class itself look like this (getters, constructors, and utility methods are stripped out for brevity):

public class EngineRuntimeException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    private final String incidentReportId;
    private final ErrorCode code;
    private final Map<String, Object> additionalInfo;

    ...

    public static EngineRuntimeException wrap(Throwable exception, ErrorCode errorCode) {
        if(exception instanceof EngineRuntimeException) {
            EngineRuntimeException se = (EngineRuntimeException)exception;
            if(errorCode != null && errorCode != se.getCode()) {
                return new EngineRuntimeException(exception.getMessage(), exception, errorCode);
            }
            return se;
        } else {
            return new EngineRuntimeException(exception.getMessage(), exception, errorCode);
        }
    }


    public static EngineRuntimeException wrap(Throwable exception) {
        return wrap(exception, SystemErrorCode.E_UNKNOWN_ERROR);
    }

Using the wrap utility method I can easily encapsulate exceptions in the different layers without loosing the original stack and without having it dirty by rethrowing or incapsulating exception more than once.

try {
  if(a == null) {
    throw new EngineRuntimeException(SystemErrorCode.E_UNKNOWN_ERROR, "blah, blah, blah");
  }

  // Some other code that can throw exceptions (checked or unchecked),
  // these exceptions will be wrapped inside EngineRuntimeException if they
  // are not already of that type.

} catch (Exception ex) {
    throw EngineRuntimeException.wrap(ex);
}    

To recap, each layer has a safety boundary try-catch that wraps Exceptions into EngineRuntimeException, just rethrowing what is already an EngineRuntimeException.

The question is: where should I log the exception? I want to log the exception only once so I was thinking about do the logging stuff inside the constructor of EngineRuntimeException class itself. Is that a bad idea? Is it better to do the logging stuff only in the catch blocks of the layer boundaries?

Also I don't want to simply log using a log4j but I want to use a service (@Service) injected by spring so that I can decide to do something more than just logging on file system. How can I do that since I can't make spring inject something in a class I have created with the "new" instruction.

  • 2
    You are asking two questions (where to put the logging and how to realize the logging through a spring-injected servive). The second of those is more appropriate for StackOverflow as it goes into implementation details, so I would advise to ask that in a separate question over there. – Bart van Ingen Schenau Sep 4 '14 at 15:39
1

If you don't mind having another library dependence, you could use AspectJ to log any exceptions that derive from EngineRuntimeException with the following declaration:

public before() throws EngineRuntimeException: mypointcut() {...}

The reason why this method is preferable is to decouple the logging from the actual exception itself.

You could of course also log directly in EngineRuntimeException and though you shouldn't run into any problems, if you need to create another exception that doesn't derive from EngineRuntimeException, that too would need to reuse the same log whereas with AspectJ, you could have a cleaner solution overall with all "logging" together in one place. You can generalize AspectJ also for other parts of your program in an unintrusive way.

Note that if you don't want AspectJ to impact performance, there are ways you can apply aspectJ to jars while being built in such a way to not require AspectJ in production at all. In this way you wouldn't need to use Spring at all.

  • I had completely forgotten about AOP. This could be a very elegant way to handle my problem. Do you think that by using it with just this exception I will suffer performance degradation? This application should be very fast. I never used AOP before, this might be the right time to learn it. – Nicola Sep 5 '14 at 9:38
  • @Nicola Not sure about other libraries, but with AspectJ it is possible to modify the jars in such a way that your code changes to make it work without dependency on AspectJ. In other words, in your code you'll have the pointcut, but in the jar produced, it'll be as if you had called a method that does what the pointcut does. So as far as performance impact is concerned when configured this way, it is minimal. If it is slow after adding logging, then it is likely going to be slow logging a message within EngineRuntimeException constructor. Hope that helps! – Neil Sep 5 '14 at 10:36
  • in the end I did it with AspectJ and I works really well. I don't have any idea yet on how to precompile the aspects to avoid AspectJ at runtime but for now I don't suffer performance degradation. Thanks – Nicola Sep 18 '14 at 10:27
  • @Nicola I'm glad it worked out for you. If I'm not mistaken, the latency caused by aspectJ is only when the jar is loaded. Though if you want to completely eliminate any latency, search for aspectJ compile-time weaving. Also see here for an example using Maven. – Neil Sep 18 '14 at 12:05
0

The question is: where should I log the exception? I want to log the exception only once

Log the exception only once, namely at place where it gets handled (not rethrown). This is the place where you have the most information.

so I was thinking about do the logging stuff inside the constructor of EngineRuntimeException class itself. Is that a bad idea?

Yes. This leads to logging it multiple times.

Also I don't want to simply log using a log4j but I want to use a service (@Service) injected by spring so that I can decide to do something more than just logging on file system.

That looks like pure boilerplate to me. I'd bet log4j can be configured to log to whatever you want. If it's not enough, switch to slfj which allows you to use any of the most common 4+ logging frameworks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.