【问题标题】:Logging the request in case of an internal server error in JAX-RS application在 JAX-RS 应用程序中出现内部服务器错误时记录请求
【发布时间】:2020-06-12 13:56:20
【问题描述】:

每当我的 rest 应用程序抛出内部错误时,我都想记录导致它失败的确切请求。为此,我创建了一个 ContainerResponseFilter。它检查响应状态是否大于或等于 500。如果是,它会序列化 ContainerRequestContext 并将其持久化到 S3。

@Provider
@ApplicationScoped
public class GlobalContainerResponseFilter implements ContainerResponseFilter {
    @Override
    public void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
        if (responseContext.getStatus() >= 500) {
            ContainerRequestContextSerialiser serialiser = new ContainerRequestContextSerialiser();
            // creates a JSON with the URI, headers, form parameters, body of the request, and all the information available on ContainerRequestContext
            String json = serialiser.serialise(requestContext);
            AwsS3Client client = new AwsS3Client();
            client.persist(json, LocalDateTime.now());
        }
    }
}

我很难处理实体输入流。如果它还没有被读取,我不需要序列化它,因为它不是异常的原因。例如,应用程序可能无法解析查询参数。如果它被读取,我想序列化它,但我不能,因为无法再次读取流。 我的第一个想法是创建一个 ContainerRequestFilter,如下所示:

@Provider
@ApplicationScoped
public class GlobalContainerRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter (ContainerRequestContext requestContext) {
        requestContext.setEntityStream(new InputStreamThatCanBeReadAgain(requestContext.getEntityStream()));        
    }

    public static class InputStreamThatCanBeReadAgain extends InputStream {

        private final InputStream source;
        private final ByteArrayOutputStream os = new ByteArrayOutputStream();

        public InputStreamThatCanBeReadAgain (InputStream source) {
            this.source = source;
        }

        @Override
        public int read () throws IOException {
            int read = source.read();
            os.write(read);
            return read;
        }

        public InputStream getInputStreamToBeReadAgain () {
            return new ByteArrayInputStream(os.toByteArray());
        }
    }
}

然后,在 ContainerResponseFilter 上,我将实体流转换为 InputStreamThatCanBeReadAgain 并获取我需要的数据。但这会为每个请求复制内存中的实体信息,即使是那些不会产生内部错误的请求,这应该是其中的绝大多数。

那么,还有其他方法可以完成这项工作吗?我想实体信息已经存在于某个地方,我只需要获取它的字节。

【问题讨论】:

    标签: jax-rs quarkus


    【解决方案1】:

    我能够使用下面的代码在我的 Quarkus 应用程序中处理所有容器和客户端请求。

    package br.com.mobiauto.bankflow.jaxrs;
    
    import br.com.mobiauto.bankflow.Resources;
    import br.com.mobiauto.bankflow.model.entity.state.IntegrationLog;
    import br.com.mobiauto.bankflow.repository.abstraction.state.IntegrationLogRepository;
    import br.com.mobiauto.lib.jackson.WriterType;
    import br.com.mobiauto.lib.utils.DateUtils;
    import br.com.mobiauto.lib.utils.JavaUtils;
    import com.fasterxml.jackson.databind.ObjectWriter;
    import org.jboss.logging.Logger;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.inject.Inject;
    import javax.transaction.Transactional;
    import javax.ws.rs.client.ClientRequestContext;
    import javax.ws.rs.client.ClientResponseContext;
    import javax.ws.rs.client.ClientResponseFilter;
    import javax.ws.rs.container.ContainerRequestContext;
    import javax.ws.rs.container.ContainerRequestFilter;
    import javax.ws.rs.container.ContainerResponseContext;
    import javax.ws.rs.container.ContainerResponseFilter;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.MultivaluedMap;
    import javax.ws.rs.core.UriInfo;
    import javax.ws.rs.ext.Provider;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URI;
    import java.nio.charset.StandardCharsets;
    import java.util.stream.Collectors;
    
    @Provider
    @ApplicationScoped
    public class IntegrationLogFilter implements ContainerRequestFilter, ContainerResponseFilter, ClientResponseFilter {
    
        private static final Logger logger = Logger.getLogger(IntegrationLogFilter.class);
    
        @WriterType
        @Inject ObjectWriter objectWriter;
        @Inject IntegrationLogRepository repository;
    
        @Override
        public void filter(ContainerRequestContext request) throws IOException {
            if (containerRequestShouldBeLogged(request) && containerRequestHasSupportedEntity(request)) {
                request.setEntityStream(new MyLoggingStream(request.getEntityStream()));
            }
        }
    
        @Override
        public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
            if (containerRequestShouldBeLogged(request)) {
                try {
                    IntegrationLog log = new IntegrationLog();
                    log.setDateTime(DateUtils.nowInBrazil().toLocalDateTime());
                    log.setRequestHeaders(toString(request.getHeaders()));
                    log.setRequestMethod(request.getMethod());
                    if (containerRequestHasSupportedEntity(request)) {
                        log.setRequestBodyJson(((MyLoggingStream) request.getEntityStream()).getString());
                    }
                    log.setRequestUrl(getRequestUrl(request.getUriInfo()));
                    log.setResponseHeaders(toString(response.getStringHeaders()));
                    if (containerResponseHasSupportedEntity(response)) {
                        log.setResponseBodyJson(objectWriter.writeValueAsString(response.getEntity()));
                    }
                    persist(log);
                } catch (Exception e) {
                    logger.error("Could not log the container request!", e);
                }
            }
        }
    
        @Override
        public void filter(ClientRequestContext request, ClientResponseContext response) throws IOException {
            try {
                IntegrationLog log = new IntegrationLog();
                log.setDateTime(DateUtils.nowInBrazil().toLocalDateTime());
                log.setRequestHeaders(toString(request.getStringHeaders()));
                log.setRequestMethod(request.getMethod());
                if (clientRequestHasSupportedEntity(request)) {
                    log.setRequestBodyJson(objectWriter.writeValueAsString(request.getEntity()));
                }
                log.setRequestUrl(getRequestUrl(request.getUri()));
                log.setResponseHeaders(toString(response.getHeaders()));
                if (clientResponseHasSupportedEntity(response)) {
                    String responseJson = JavaUtils.toString(response.getEntityStream(), StandardCharsets.UTF_8.name());
                    log.setResponseBodyJson(responseJson);
                    response.setEntityStream(new ByteArrayInputStream(responseJson.getBytes(StandardCharsets.UTF_8)));
                }
                persist(log);
            } catch (Exception e) {
                logger.error("Could not log the client request!", e);
            }
        }
    
        @Transactional
        public void persist(IntegrationLog entity) {
            repository.persist(entity);
        }
    
        private String getRequestUrl(URI uri) {
            return uri.toString().replace(Resources.INTEGRATIONS, "");
        }
    
        private String getRequestUrl(UriInfo uriInfo) {
            String baseUri = uriInfo.getBaseUri().toString();
            String requestUri = uriInfo.getRequestUri().toString();
            return requestUri.replace(baseUri, "").replace(Resources.INTEGRATIONS, "");
        }
    
        private boolean clientRequestHasSupportedEntity(ClientRequestContext request) {
            return request.hasEntity() && request.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE);
        }
    
        private boolean containerRequestHasSupportedEntity(ContainerRequestContext request) {
            return request.hasEntity() && request.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE);
        }
    
        private boolean clientResponseHasSupportedEntity(ClientResponseContext response) {
            return response.hasEntity() && response.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE);
        }
    
        private boolean containerResponseHasSupportedEntity(ContainerResponseContext response) {
            return response.hasEntity() && response.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE);
        }
    
        private boolean containerRequestShouldBeLogged(ContainerRequestContext containerRequestContext) {
            UriInfo uriInfo = containerRequestContext.getUriInfo();
            String requestUri = uriInfo.getRequestUri().toString();
            return requestUri.contains(Resources.INTEGRATIONS);
        }
    
        private String toString(MultivaluedMap<String, String> map) {
            return map.
                    entrySet().
                    stream().
                    map(entry -> entry.getKey() + ":" + String.join(",", entry.getValue())).
                    collect(Collectors.joining(";"));
        }
    
        private static class MyLoggingStream extends InputStream {
    
            private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            private final InputStream inputStream;
    
            public MyLoggingStream(final InputStream inputStream) {
                this.inputStream = inputStream;
            }
    
            public String getString() {
                return baos.toString(StandardCharsets.UTF_8);
            }
    
            @Override
            public int read() throws IOException {
                int read = inputStream.read();
                if (read > -1) {
                    baos.write(read);
                }
                return read;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-25
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多