Monday 3 April 2017

java - How should I log uncaught exceptions in my RESTful JAX-RS web service?

I have a RESTful web service running under Glassfish 3.1.2 using Jersey and Jackson:



@Stateless
@LocalBean
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("users")
public class UserRestService {
private static final Logger log = ...;

@GET
@Path("{userId:[0-9]+}")
public User getUser(@PathParam("userId") Long userId) {
User user;

user = loadUserByIdAndThrowApplicableWebApplicationExceptionIfNotFound(userId);

return user;
}
}


For expected exceptions, I throw the appropriate WebApplicationException, and I'm happy with the HTTP 500 status that is returned if an unexpected exception occurs.



I would now like to add logging for these unexpected exceptions, but despite searching, cannot find out how I should be going about this.



Fruitless Attempt



I have tried using a Thread.UncaughtExceptionHandler and can confirm that it is applied inside the method body, but its uncaughtException method is never called, as something else is handling the uncaught exceptions before they reach my handler.



Other Ideas: #1



Another option I've seen some people use is an ExceptionMapper, which catches all exceptions and then filters out WebApplicationExceptions:



@Provider
public class ExampleExceptionMapper implements ExceptionMapper {
private static final Logger log = ...;

public Response toResponse(Throwable t) {
if (t instanceof WebApplicationException) {
return ((WebApplicationException)t).getResponse();
} else {
log.error("Uncaught exception thrown by REST service", t);

return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
// Add an entity, etc.
.build();
}
}
}


While this approach may work, it feels to me like misuse of what ExceptionMappers are supposed to be used for, that is, mapping certain exceptions to certain responses.



Other Ideas: #2



Most sample JAX-RS code returns the Response object directly. Following this approach, I could change my code to something like:



public Response getUser(@PathParam("userId") Long userId) {
try {
User user;

user = loadUserByIdAndThrowApplicableWebApplicationExceptionIfNotFound(userId);

return Response.ok().entity(user).build();
} catch (Throwable t) {
return processException(t);
}
}

private Response processException(Throwable t) {
if (t instanceof WebApplicationException) {
return ((WebApplicationException)t).getResponse();
} else {
log.error("Uncaught exception thrown by REST service", t);

return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
// Add an entity, etc.
.build();
}
}


However, I'm leery of going this route, as my actual project is not as simple as this example, and I would have to implement this same pattern over and over again, not to mention having to manually build up the Responses.



What should I do?



Are there better methods for adding logging for uncaught exceptions?
Is there a "right" way of implementing this?

No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...