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.