【问题标题】:Jersey... how to log all exceptions, but still invoke ExceptionMappers泽西岛...如何记录所有异常,但仍调用 ExceptionMappers
【发布时间】:2014-05-27 17:04:49
【问题描述】:

我有点困...想要我的蛋糕,也吃掉它。

我想记录我的应用程序抛出的所有个异常。因此,如果有人点击了错误的 URL,我想将堆栈跟踪记录到 SLF4J。

所以你可能在想,'嘿,这很简单,只需实现一个异常映射器并记录异常。“所以我做到了:

public class RestExceptionMapper implements ExceptionMapper<java.lang.Exception> {
    private static final Logger log = LoggerFactory.getLogger(RestExceptionMapper.class);

    /**
     * {@inheritDoc}
     */
    @Override
    public Response toResponse(Exception exception) {
        log.error("toResponse() caught exception", exception);
        return null;
    }
}

如果您这样做,当有人输入错误的 URL 时,不会出现 404 错误,而是会收到 500 错误。有人会猜测返回 null 会将异常传播到链处理程序中,但 Jersey 不会这样做。它实际上提供的信息很少,为什么它会选择一个处理程序而不是另一个处理程序......

有没有人遇到过这个问题,你是怎么解决的?

【问题讨论】:

  • 我会使用 CDI 拦截器
  • 那么,您的问题是什么:是 404 错误吗?还是如何选择异常映射器?还是别的什么?
  • 不,问题是如果您使用上述代码,则不会调用 Jersey 提供的“stock”拦截器。 404 错误变成 500 错误。

标签: java rest jakarta-ee jersey jax-rs


【解决方案1】:

要返回正确的 http 状态代码,您的异常映射器可能如下所示:

@Provider
public class RestExceptionMapper implements ExceptionMapper<Throwable>
{
    private static final Logger log = LoggerFactory.getLogger(RestExceptionMapper.class);

    @Override
    public Response toResponse(Throwable exception)
    {
        log.error("toResponse() caught exception", exception);

        return Response.status(getStatusCode(exception))
                .entity(getEntity(exception))
                .build();
    }

    /*
     * Get appropriate HTTP status code for an exception.
     */
    private int getStatusCode(Throwable exception)
    {
        if (exception instanceof WebApplicationException)
        {
            return ((WebApplicationException)exception).getResponse().getStatus();
        }

        return Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
    }

    /*
     * Get response body for an exception.
     */
    private Object getEntity(Throwable exception)
    {
        // return stack trace for debugging (probably don't want this in prod...)
        StringWriter errorMsg = new StringWriter();
        exception.printStackTrace(new PrintWriter(errorMsg));
        return errorMsg.toString();            
    }
}

听起来您对级联异常映射器感兴趣,但根据规范这是不可能的:

JAX-RS 2.0 规范,第 4.4 章

"异常映射提供程序将已检查或运行时异常映射到 Response 实例。异常 映射提供者实现了 ExceptionMapper 接口并且可以用 @Provider 用于自动发现。在选择异常映射提供程序来映射异常时, 实现必须使用泛型类型是异常的最近超类的提供者。

当资源类或提供者方法抛出异常时,存在异常映射 provider,匹配的provider用于获取一个Response实例。处理结果响应 就像 Web 资源方法返回了响应一样,请参阅第 3.3.3 节。特别是,一个映射 必须使用第 6 章中定义的 ContainerResponse 过滤器链来处理响应。

为避免潜在的无限循环,在处理 请求及其相应的响应。 JAX-RS 实现不得尝试映射异常 在处理先前从异常映射的响应时抛出。相反,这个异常必须 按照第 3.3.4 节中的步骤 3 和 4 进行处理。"

【讨论】:

    【解决方案2】:

    您可以使用RequestEventListener 来侦听异常事件并记录 throwable,而不会干扰任何现有处理。请注意,这意味着首先注册一个ApplicationEventListener,然后返回一个RequestEventListener 的实例。以下都在一个类中实现:

    @Provider
    public class ExceptionLogger implements ApplicationEventListener, RequestEventListener {
        
        private static final Logger log = LoggerFactory.getLogger(RequestExceptionLogger.class);
        
        @Override
        public void onEvent(final ApplicationEvent applicationEvent) {
        }
    
        @Override
        public RequestEventListener onRequest(final RequestEvent requestEvent) {
            return this;
        }
    
        @Override
        public void onEvent(RequestEvent paramRequestEvent) {
            if(paramRequestEvent.getType() == Type.ON_EXCEPTION) {
                log.error("", paramRequestEvent.getException());
            }
        }
    }
    

    【讨论】:

    • 我认为这是更好的答案,因为 OP 不会尝试将异常映射到特定的 HTTP 错误,而只是记录它。
    • “我想记录我的应用程序抛出的所有异常。”这实际上回答了这个问题。
    • 您可以通过添加 @Provider 并确保该类在您在 ResourceConfig 中定义的“rest”包中来注册它。
    猜你喜欢
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 2012-06-04
    • 2014-10-06
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 2015-12-28
    相关资源
    最近更新 更多