去除Dubbox LoggingFilter自动打印response内容
Dubbox自带日志打印问题
dubbo配置文件配置protocol引入LoggingFilter:
<dubbo:protocol accepts="500" contextpath="xxx" name="rest" port="xxx" server="tomcat"
threads="500" extension="com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter"/>
LoggingFilter源码:
public class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, ClientResponseFilter, WriterInterceptor, ReaderInterceptor {
// 省略其他代码
···
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
ServerReaderInterceptorContext scontext =(ServerReaderInterceptorContext)context;
Field fd = null;
String path ="";
try {
fd = ServerReaderInterceptorContext.class.getDeclaredField("request");
fd.setAccessible(true);
HttpRequest rm = (HttpRequest) fd.get(scontext);
path = rm.getUri().getPath();
} catch (Exception e) {
logger.warn("Get url path is exception,but your code is OK! ",e);
}
byte[] buffer = IOUtils.toByteArray(context.getInputStream());
logger.info("The HTTP("+path+") request body are: \nBody:"+new String(buffer, "UTF-8"));
context.setInputStream(new ByteArrayInputStream(buffer));
return context.proceed();
}
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
ServerWriterInterceptorContext scontext = (ServerWriterInterceptorContext)context;
Field fd = null;
String path ="";
try {
fd = ServerWriterInterceptorContext.class.getDeclaredField("request");
fd.setAccessible(true);
HttpRequest rm = (HttpRequest) fd.get(scontext);
path = rm.getUri().getPath();
} catch (Exception e) {
logger.warn("Get url path is exception,but your code is OK! ",e);
}
OutputStreamWrapper wrapper = new OutputStreamWrapper(context.getOutputStream());
context.setOutputStream(wrapper);
context.proceed();
logger.info("The url("+path+") of response body is: \n" + new String(wrapper.getBytes(), "UTF-8") + "\n");
}
protected void logHttpHeaders(String url,MultivaluedMap<String, String> headers) {
StringBuilder msg = new StringBuilder("The HTTP("+url+") request body are: \nHeader:");
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
msg.append(entry.getKey()).append(": ");
for (int i = 0; i < entry.getValue().size(); i++) {
msg.append(entry.getValue().get(i));
if (i < entry.getValue().size() - 1) {
msg.append(", ");
}
}
msg.append("\n");
}
logger.info(msg.toString());
}
// 省略其他代码
···
}
- 其中大量的log info日志打印request body和response body,在实际应用中,这些不起眼的日志有可能会撑爆整个服务的文件内存,拖垮接口的请求响应效率。如文件上传与下载接口,将文件内容打印到日志中时完全没有必要的。
- 因此,需要对默认的日志记录进行改造,去掉不需要的日志记录。
自定义日志记录类
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
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.MultivaluedMap;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.core.interception.ServerReaderInterceptorContext;
import org.jboss.resteasy.core.interception.ServerWriterInterceptorContext;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
public class CustomLogFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, ClientResponseFilter, WriterInterceptor, ReaderInterceptor {
private Logger logger = LoggerFactory.getLogger(CustomLogFilter.class);
public void filter(ClientRequestContext context) throws IOException {
logHttpHeaders(context.getUri().getPath(), context.getStringHeaders());
}
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
logHttpHeaders(requestContext.getUri().getPath(), responseContext.getHeaders());
}
public void filter(ContainerRequestContext context) throws IOException {
logHttpHeaders(context.getUriInfo().getPath(), context.getHeaders());
}
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
logHttpHeaders(requestContext.getUriInfo().getPath(), responseContext.getStringHeaders());
}
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
Field fd = null;
try {
fd = ServerReaderInterceptorContext.class.getDeclaredField("request");
fd.setAccessible(true);
} catch (Exception e) {
logger.warn("Get url path is exception,but your code is OK! ", e);
}
byte[] buffer = IOUtils.toByteArray(context.getInputStream());
context.setInputStream(new ByteArrayInputStream(buffer));
return context.proceed();
}
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
Field fd = null;
try {
fd = ServerWriterInterceptorContext.class.getDeclaredField("request");
fd.setAccessible(true);
} catch (Exception e) {
logger.warn("Get url path is exception,but your code is OK! ", e);
}
OutputStreamWrapper wrapper = new OutputStreamWrapper(context.getOutputStream());
context.setOutputStream(wrapper);
context.proceed();
}
protected void logHttpHeaders(String url, MultivaluedMap<String, String> headers) {
StringBuilder msg = new StringBuilder("The HTTP(" + url + ") request body are: \nHeader:");
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
msg.append(entry.getKey()).append(": ");
for (int i = 0; i < entry.getValue().size(); i++) {
msg.append(entry.getValue().get(i));
if (i < entry.getValue().size() - 1) {
msg.append(", ");
}
}
msg.append("\n");
}
logger.info(msg.toString());
}
protected static class OutputStreamWrapper extends OutputStream {
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final OutputStream output;
private OutputStreamWrapper(OutputStream output) {
this.output = output;
}
@Override
public void write(int i) throws IOException {
buffer.write(i);
output.write(i);
}
@Override
public void write(byte[] b) throws IOException {
buffer.write(b);
output.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
buffer.write(b, off, len);
output.write(b, off, len);
}
@Override
public void flush() throws IOException {
output.flush();
}
@Override
public void close() throws IOException {
output.close();
}
public byte[] getBytes() {
return buffer.toByteArray();
}
}
}
- 为了不影响其他接口,参照LoggingFilter类,实现的类和方法内容不变,直接删除log.info部分的内容,即可去除不必要的request和response日志打印。
- 再将dubbo配置文件的中的extension配置替换为自定义的日志类即可。
修改dubbo配置文件
<dubbo:protocol accepts="500" contextpath="xxx" extension="xxxxxx.CustomLogFilter"
name="rest" port="xxx" server="tomcat" threads="500"/>
测试
找到项目的日志文件,清空内容,重新启动服务,调用接口,查看日志文件中是否还包含“request body”或“response body”内容。