Reputation: 1625
I am following a standard that requires me to log the class#method that originates an exception for easy debugging from our UI. Without pulling the logs I know what method spawned the exception and can (usually) easily find the root cause.
However, I would like a dynamic way of determining the method signature because I am currently hard-coding it everywhere I am throwing an exception. Is there a better way.
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findUser(Long id){
if(id == null || id < 1){
throw new BadRequestException("UserService#findUser", String.format(
"Invalid Id: %d", id));
}
User user = userRepository.findOne(id);
if(user == null){
throw new DataNotFoundException("UserService#findUser", String.format(
"Can't Find User for Id: %d", id));
}
return user;
}
}
--
The goal is that I can create any custom exception and within that constructor I can infer the method signature by where the exception was created from.
public BadRequestException(String issue){
super(String.format("%s | Bad Request | %s", <<signature>>, issue);
}
--
UserService class --> findUser method --> BadRequest("UserService#findUser")
UserService class --> createUser method --> BadRequest("UserService#createUser")
Upvotes: 2
Views: 400
Reputation: 131336
You could introduce a helper method that retrieves the method and the class names from the current thread :
private static MethodAndClassName retrieveMethodAndClassName() {
StackTraceElement[] allStackTraces = Thread.currentThread().getStackTrace();
StackTraceElement currentMethod = allStackTraces[2];
return new MethodAndClassName(currentMethod.getMethodName(), currentMethod.getClassName());
}
Note that the method invocations are stacked. I get the third last nested call (allStackTraces[2]
) because the last call (allStackTraces[0]
) is Thread.getStackTrace()
, the before last call (allStackTraces[1]
) is retrieveMethodAndClassName()
and the before before last call (allStackTraces[2]
) is what you look for : the method that invoked retrieveMethodAndClassName()
.
MethodAndClassName
is a custom class that defines needed information to trace :
public class MethodAndClassName {
private String method;
private String clazz;
public MethodAndClassName(String method, String clazz) {
this.method = method;
this.clazz = clazz;
}
public String getMethodName() {
return method;
}
public String getClassName() {
return clazz;
}
}
And in your client code use it such as :
public User findUser(Long id){
if(id == null || id < 1){
throw new BadRequestException(ReflectionHelper.retrieveMethodAndClassName(), String.format(
"Invalid Id: %d", id));
}
User user = userRepository.findOne(id);
if(user == null){
throw new DataNotFoundException(ReflectionHelper.retrieveMethodAndClassName(), String.format(
"Can't Find User for Id: %d", id));
}
return user;
}
Upvotes: 3
Reputation: 2923
Use Throwable.getStackTrace(). This will return an array of StackTraceElement, and the first entry will point to where the the Throwable was instantiated.
Upvotes: 0