我为 ResourceConfig 注册了 LogginFilter 过滤器。
// Enable logging of requests
registerInstances(new LoggingFilter(java.util.logging.Logger.getLogger(this.getClass().getName()),
true));
我还启用了 GZIPEncoding 过滤器,因此将为支持它的客户端压缩响应。
// Encode gzip responses if request header supports it
EncodingFilter.enableFor(this, GZipEncoder.class);
但现在的问题是日志过滤器正在输出压缩的响应实体,即。在应用压缩之后而不是之前,所以输出看起来像这样使得它非常无用并且不可能看到返回的内容。
INFO: 78 * Server responded with a response on thread http-bio-8443-exec-5
78 < 200
78 < Content-Type: application/json
^_^@^@^@^@^@^@^@
我可以禁用 GZIPEncoder 并以未压缩的方式记录响应,然后响应实体也以未压缩的方式发送...
有没有人建议如何避免这个问题并使服务器端 GZIP 支持成为可能,同时以纯文本而不是压缩的形式调试响应数据。
我将不胜感激。
谢谢 问候
最佳答案
我们也有同样的要求,在压缩时记录明文有效负载。这就是我最终的做法。 此示例适用于 http 客户端请求 Accept-Encoding: gzip 的情况。
您将拥有 GZIPWriterFilterInterceptor 类,它实现了 Jersey Filter 和 Interceptor 接口(interface)。
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map.Entry;
import java.util.UUID;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.commons.io.output.TeeOutputStream;
@Provider
@Priority(Priorities.ENTITY_CODER)
@Compress
public class GZIPWriterFilterInterceptor implements ContainerResponseFilter, WriterInterceptor
{
/**
* http header value for Accept-Encoding
*/
public static final String GZIP = "gzip";
protected GZIPOutputStream getGZIPOutputStream(final OutputStream outputStream) throws IOException
{
return new GZIPOutputStream(outputStream);
}
@Override
public void filter(final ContainerRequestContext requestContext,
final ContainerResponseContext responseContext) throws IOException
{
final MultivaluedMap<String,String> headers = requestContext.getHeaders();
//if client has requested with HttpHeader of Accept-Encoding: gzip,
//then do gzip encoding and put Content-Coding: gzip as part of response
if( headers != null && headers.get(HttpHeaders.ACCEPT_ENCODING) != null )
{
for(final String header : headers.get(HttpHeaders.ACCEPT_ENCODING))
{
if(header.contains(GZIP))
{
responseContext.getHeaders().add(HttpHeaders.CONTENT_ENCODING, GZIP);
break;
}
}
}
}
@Override
public void aroundWriteTo(final WriterInterceptorContext context)
throws IOException, WebApplicationException
{
if( context.getHeaders() != null && context.getHeaders().containsKey(HttpHeaders.CONTENT_ENCODING)
&& context.getHeaders().get(HttpHeaders.CONTENT_ENCODING).contains(GZIP) )
{
// for the response, there is only one outputstream i.e. written to by jersey after all the
// interceptors have invoked context.proceed() in their aroundWriteTo method.
// so when we wrap the base outputstream in a GZIPOutputStream, the response will be GZIP encoded.
// but we also want to log the payload. Hence we use TeeOutputStream to "clone" the base outputstream.
// As a result, jersey will write directly to TeeOutputStream, which in turn will forward the writes to
// both GZIPOutputStream and ByteArrayOutputStream. We also store the ByteArrayOutputStream in context
// so that it can be accessed by APILoggingFilter.
final ByteArrayOutputStream boas = new ByteArrayOutputStream();
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new TeeOutputStream(getGZIPOutputStream(outputStream)
, boas) );
context.setProperty(Constants.ENTITY_LOGGER_PROPERTY, boas);
}
context.proceed();
}
}
请注意,我们如何使用 Apache 的 TeeOutputStream 克隆输出流。
接下来您需要的只是 Compress Annotation 定义,然后您可以使用 @Compress 注释您的 Resource 方法。还要在您的 web.xml 中包含此类的包名称。
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress
{
}
这是 web.xml 的相关片段
<servlet>
<servlet-name>MY Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.my.gzip.logging.jaxrs.filters.package</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
如果你想进行负载日志记录,你可以添加另一个 Jersey 过滤器/拦截器。这是它的 aroundWriteTo 方法的样子。
public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException
{
if (context != null)
{
ByteArrayOutputStream stream = (ByteArrayOutputStream) context.getProperty(ENTITY_LOGGER_PROPERTY);
if( stream == null)
{
//it means the GZIPWriter is not hooked in.
stream = new ByteArrayOutputStream();
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new TeeOutputStream(outputStream, stream) );
}
context.proceed();
final String message = stream == null ? "no entity" : stream.toString();
Logger.end(context, message);
}
}
关于java - Jersey 2 日志记录和 gzip,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28091959/