我有一个使用 Jersey 和 Jackson 在 Glassfish 3.1.2 下运行的 RESTful Web 服务:
@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;
}
}
对于预期的异常,我抛出相应的 WebApplicationException
,如果发生意外异常,我对返回的 HTTP 500 状态感到满意。
我现在想为这些意外异常添加日志记录,但尽管进行了搜索,但无法找到我应该如何处理这个问题。
徒劳的尝试
我尝试过使用 Thread.UncaughtExceptionHandler
并且可以确认它是在方法体内部应用的,但它的 uncaughtException
方法永远不会被调用,因为在未捕获的异常到达我的处理程序之前有其他东西正在处理它们。
其他想法:#1
我看到有些人使用的另一个选项是 ExceptionMapper
,它会捕获所有异常,然后过滤掉 WebApplicationExceptions:
@Provider
public class ExampleExceptionMapper implements ExceptionMapper<Throwable> {
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();
}
}
}
虽然这种方法可能有效,但在我看来,这就像滥用 ExceptionMappers 的用途,即将某些异常映射到某些响应。
其他想法:#2
大多数示例 JAX-RS 代码返回 Response
直接反对。按照这种方法,我可以将我的代码更改为:
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();
}
}
但是,我对走这条路持怀疑态度,因为我的实际项目不像这个例子那么简单,我必须一遍又一遍地实现相同的模式,更不用说必须手动构建响应.
我该怎么办?
有没有更好的方法来为未捕获的异常添加日志记录? 有没有“正确”的方式来实现这一点?
最佳答案
由于缺乏更好的方法来实现未捕获的 JAX-RS 异常的日志记录,请使用包罗万象的 ExceptionMapper
如 Other Ideas: #1 似乎是添加此功能的最简洁、最简单的方法。
这是我的实现:
@Provider
public class ThrowableExceptionMapper implements ExceptionMapper<Throwable> {
private static final Logger log = Logger.getLogger(ThrowableExceptionMapper.class);
@Context
HttpServletRequest request;
@Override
public Response toResponse(Throwable t) {
if (t instanceof WebApplicationException) {
return ((WebApplicationException) t).getResponse();
} else {
String errorMessage = buildErrorMessage(request);
log.error(errorMessage, t);
return Response.serverError().entity("").build();
}
}
private String buildErrorMessage(HttpServletRequest req) {
StringBuilder message = new StringBuilder();
String entity = "(empty)";
try {
// How to cache getInputStream: http://stackoverflow.com/a/17129256/356408
InputStream is = req.getInputStream();
// Read an InputStream elegantly: http://stackoverflow.com/a/5445161/356408
Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
entity = s.hasNext() ? s.next() : entity;
} catch (Exception ex) {
// Ignore exceptions around getting the entity
}
message.append("Uncaught REST API exception:\n");
message.append("URL: ").append(getOriginalURL(req)).append("\n");
message.append("Method: ").append(req.getMethod()).append("\n");
message.append("Entity: ").append(entity).append("\n");
return message.toString();
}
private String getOriginalURL(HttpServletRequest req) {
// Rebuild the original request URL: http://stackoverflow.com/a/5212336/356408
String scheme = req.getScheme(); // http
String serverName = req.getServerName(); // hostname.com
int serverPort = req.getServerPort(); // 80
String contextPath = req.getContextPath(); // /mywebapp
String servletPath = req.getServletPath(); // /servlet/MyServlet
String pathInfo = req.getPathInfo(); // /a/b;c=123
String queryString = req.getQueryString(); // d=789
// Reconstruct original requesting URL
StringBuilder url = new StringBuilder();
url.append(scheme).append("://").append(serverName);
if (serverPort != 80 && serverPort != 443) {
url.append(":").append(serverPort);
}
url.append(contextPath).append(servletPath);
if (pathInfo != null) {
url.append(pathInfo);
}
if (queryString != null) {
url.append("?").append(queryString);
}
return url.toString();
}
}
关于java - 我应该如何在我的 RESTful JAX-RS Web 服务中记录未捕获的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19621653/