以下代码导致在Dropwizard 0.9.2和1.0.2中打印JSON服务器响应:
return ClientBuilder
.newBuilder()
.build()
.register(new LoggingFilter(Logger.getLogger(LoggingFilter.class.getName()), true))
例如:
Oct 21, 2016 7:57:42 AM org.glassfish.jersey.filter.LoggingFilter log
INFO: 1 * Client response received on thread main
1 < 401
1 < Connection: keep-alive
1 < Content-Length: 49
1 < Content-Type: text/plain
1 < Date: Fri, 21 Oct 2016 07:57:42 GMT
1 < Server: […]
1 < WWW-Authenticate: Basic realm="[…]"
Credentials are required to access this resource.
javax.ws.rs.NotAuthorizedException: HTTP 401 Unauthorized
但是,LoggingFilter
在1.0.2中已弃用,建议您使用LoggingFeature
。在documentation of LoggingFeature中,它表示默认详细程度为LoggingFeature.Verbosity.PAYLOAD_TEXT
,因此我希望以下代码仍然在Dropwizard 1.0.2中打印JSON服务器响应:
return ClientBuilder
.newBuilder()
.build()
.register(new LoggingFeature(Logger.getLogger(getClass().getName())))
而是日志包含这个:
javax.ws.rs.NotAuthorizedException: HTTP 401 Unauthorized
答案 0 :(得分:5)
这就是诀窍:
new LoggingFeature(Logger.getLogger(getClass().getName()), Level.OFF, LoggingFeature.Verbosity.PAYLOAD_TEXT, 8192)
我猜测客户端中的日志记录功能就像过滤器,而不是预期的包含。
答案 1 :(得分:5)
一个简短的例子来说明一个常见问题,它使开发人员认为日志记录功能无效。
private static final LOG = Logger.getLogger(getClass().getName());
public void test() {
Client client = ClientBuilder.newBuilder()
.register(new LoggingFeature(LOG, Level.FINE, LoggingFeature.Verbosity.PAYLOAD_ALL, 8192))
.build();
// all requests and responses using this client will now be logged
// with the log-level FINE to the logger LOG, but the logger
// will simply ignore them, because it's default level is INFO
}
创建的记录器实例LOG
使用默认日志级别,即INFO。这意味着它将接受级别至少为INFO或更高级别的所有日志消息(WARNING,SEVERE,...),但它将忽略具有较低级别的所有消息,如FINE。 (它仍会将消息传递给它的父记录器,如果有的话)
注意:处理程序的默认日志级别是Level.ALL,他们不应该拒绝任何日志记录,只要你不修改他们的级别。
因此,您需要提高LoggingFeatures级别或降低记录器级别以查看消息。
解决方案1:提高LoggingFeature的级别:
new LoggingFeature(LOG, Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ALL, 8192)
解决方案2:降低记录器的级别:
LOG.setLevel(Level.FINE)
答案 2 :(得分:1)
所以 - 我测试了这个。这可能是有关记录器配置和功能配置的问题,而不是实际功能。我给你的提示确实打印了你想要的东西,这是证据:
public class MainClientTest {
public static void main(String[] args) {
Client build = ClientBuilder.newBuilder().build();
// Configure Logger to log it all
Logger logger = Logger.getLogger("test");
logger.setLevel(Level.ALL);
logger.setUseParentHandlers(false);
Handler[] handlers = logger.getHandlers();
for(Handler h : handlers) logger.removeHandler(h);
logger.addHandler(buildseh());
logger.info("Logger");
build = build.register(new LoggingFeature(logger, Level.ALL, Verbosity.PAYLOAD_ANY, null));
build.target("https://www.google.com").request().get();
}
public static StreamHandler buildseh() {
final StreamHandler seh = new StreamHandler(System.err, new JdkLoggerFormatter()) {
@Override
public synchronized void publish(final LogRecord record) {
super.publish(record);
flush();
}
};
seh.setLevel(Level.ALL); // Default StdErr Setting
return seh;
}
}
打印:
1477055066111 I test Logger
1477055066397 test 1 * Sending client request on thread main
1 > GET https://www.google.com
1477055067350 test 1 * Client response received on thread main
1 < 200
1 < Accept-Ranges: none
1 < Alt-Svc: quic=":443"; ma=2592000; v="36,35,34,33,32"
1 < Cache-Control: private, max-age=0
1 < Content-Type: text/html; charset=ISO-8859-1
1 < Date: Fri, 21 Oct 2016 13:04:27 GMT
1 < Expires: -1
1 < P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."
1 < Server: gws
1 < Set-Cookie: NID=89=YPr3UcI5rcA4qiaXfm9zkA0uAWrnSDEbxN3TcFuhZ9PkLNvkSHBCHHLcYeXa7tNpzpM_9p7AFAreYq3kR9awqqKrhv5W6pWavfx5bZM7Jjbt559a4aEv20exEPJRmS1N; expires=Sat, 22-Apr-2017 13:04:27 GMT; path=/; domain=.google.co.uk; HttpOnly
1 < Transfer-Encoding: chunked
1 < Vary: Accept-Encoding
1 < X-Frame-Options: SAMEORIGIN
1 < X-XSS-Protection: 1; mode=block
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-GB"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title><script>(function(){window.google={kEI:'WxIKWJOeEcGwa_nDmNgC',kEXPI:'1351633,1351901,3700318,3700400,4029815,4031109,4032678,4036527,4038012,4039268,4043492,4045841,4048347,4052304,4062185,4063220,4065786,4066665,4068550,4068816,4069839,4069841,4070140,4070329,4071229,4072287,4072364,4072602,4072705,4072773,4073248,4073405,4073758,4073913,4073959,4074684,4074809,4076096,4076315,4076930,4076999,4077037,4077119,4077
所以,这里有陷阱:
您的功能必须配置正确的级别。参见:
new LoggingFeature(logger, Level.ALL, Verbosity.PAYLOAD_ANY, null)
必须将记录器配置为记录正确的内容。
seh.setLevel(Level.ALL); // Default StdErr Setting
logger.setLevel(Level.ALL);
该功能必须具有正确的详细程度:Verbosity.PAYLOAD_ANY
这就是你需要做的一切。
此致
Artur
答案 3 :(得分:0)
对我来说,@ l0b0的解决方案确实记录了请求/响应有效负载,但是我的访问日志将请求和响应记录为错误。以下行对我来说正常工作:
new LoggingFeature(Logger.getLogger(getClass().getName()), Level.INFO, LoggingFeature.Verbosity.PAYLOAD_TEXT, 8192)
答案 4 :(得分:0)
要使用LoggingFeature记录服务器和客户端请求/响应的自定义日志,您需要创建一个自定义类来扩展LoggingFeature并实现ContainerRequestFilter,ContainerResponseFilter,ClientRequestFilter,ClientResponseFilter和WriterInterceptor。
您需要重写LoggingFeature方法public boolean configure(FeatureContext context)
,并在上下文中注册CustomLoggingFeature对象
@Override
public boolean configure(FeatureContext context) {
context.register(this);
return true;
}
ContainerRequestFilter和ContainerResponseFilter接口具有用于记录服务器请求和响应的方法。
ClientRequestFilter和ContainerResponseFilter接口具有用于记录客户端请求和响应的方法。
此外,您需要实现WriterInterceptor
来记录客户端请求和服务器响应正文。
最后用球衣注册了CustomLoggingFeature类。
environment.jersey().register(new CustomLoggingFeature(Logger.getLogger(getClass().getName()), Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 8192));
我正在使用Dropwizard v1.3.5 。
这是我的实现方式。
public class CustomLoggingFeature extends LoggingFeature implements ContainerRequestFilter, ContainerResponseFilter,
ClientRequestFilter, ClientResponseFilter, WriterInterceptor {
private static final boolean printEntity = true;
private static final int maxEntitySize = 8 * 1024;
private final Logger logger = Logger.getLogger("CustomLoggingFeature");
private static final String ENTITY_LOGGER_PROPERTY = CustomLoggingFeature.class.getName();
private static final String NOTIFICATION_PREFIX = "* ";
private static final String REQUEST_PREFIX = "> ";
private static final String RESPONSE_PREFIX = "< ";
private static final String AUTHORIZATION = "Authorization";
private static final String EQUAL = " = ";
private static final String HEADERS_SEPARATOR = ", ";
private static List<String> requestHeaders;
static {
requestHeaders = new ArrayList<>();
requestHeaders.add(AUTHORIZATION);
}
public CustomLoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {
super(logger, level, verbosity, maxEntitySize);
}
@Override
public boolean configure(FeatureContext context) {
context.register(this);
return true;
}
@Override
public void filter(final ClientRequestContext context) {
final StringBuilder b = new StringBuilder();
printHeaders(b, context.getStringHeaders());
printRequestLine(b, "Sending client request", context.getMethod(), context.getUri());
if (printEntity && context.hasEntity()) {
final OutputStream stream = new LoggingStream(b, context.getEntityStream());
context.setEntityStream(stream);
context.setProperty(ENTITY_LOGGER_PROPERTY, stream);
// not calling log(b) here - it will be called by the interceptor
} else {
log(b);
}
}
@Override
public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException {
final StringBuilder b = new StringBuilder();
printResponseLine(b, "Client response received", responseContext.getStatus());
if (printEntity && responseContext.hasEntity()) {
responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream(),
MessageUtils.getCharset(responseContext.getMediaType())));
}
log(b);
}
@Override
public void filter(final ContainerRequestContext context) throws IOException {
final StringBuilder b = new StringBuilder();
printHeaders(b, context.getHeaders());
printRequestLine(b, "Server has received a request", context.getMethod(), context.getUriInfo().getRequestUri());
if (printEntity && context.hasEntity()) {
context.setEntityStream(logInboundEntity(b, context.getEntityStream(), MessageUtils.getCharset(context.getMediaType())));
}
log(b);
}
@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {
final StringBuilder b = new StringBuilder();
printResponseLine(b, "Server responded with a response", responseContext.getStatus());
if (printEntity && responseContext.hasEntity()) {
final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream());
responseContext.setEntityStream(stream);
requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);
// not calling log(b) here - it will be called by the interceptor
} else {
log(b);
}
}
@Override
public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {
final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY);
writerInterceptorContext.proceed();
if (stream != null) {
log(stream.getStringBuilder(MessageUtils.getCharset(writerInterceptorContext.getMediaType())));
}
}
private static class LoggingStream extends FilterOutputStream {
private final StringBuilder b;
private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
LoggingStream(final StringBuilder b, final OutputStream inner) {
super(inner);
this.b = b;
}
StringBuilder getStringBuilder(Charset charset) {
// write entity to the builder
final byte[] entity = byteArrayOutputStream.toByteArray();
b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset));
if (entity.length > maxEntitySize) {
b.append("...more...");
}
b.append('\n');
return b;
}
public void write(final int i) throws IOException {
if (byteArrayOutputStream.size() <= maxEntitySize) {
byteArrayOutputStream.write(i);
}
out.write(i);
}
}
private void printHeaders(StringBuilder b, MultivaluedMap<String, String> headers) {
for (String header : requestHeaders) {
if (Objects.nonNull(headers.get(header))) {
b.append(header).append(EQUAL).append(headers.get(header)).append(HEADERS_SEPARATOR);
}
}
int lastIndex = b.lastIndexOf(HEADERS_SEPARATOR);
if (lastIndex != -1) {
b.delete(lastIndex, lastIndex + HEADERS_SEPARATOR.length());
b.append("\n");
}
}
private void log(final StringBuilder b) {
String message = Util.mask(b.toString());
if (logger != null) {
logger.info(message);
}
}
private void printRequestLine(final StringBuilder b, final String note, final String method, final URI uri) {
b.append(NOTIFICATION_PREFIX)
.append(note)
.append(" on thread ").append(Thread.currentThread().getId())
.append(REQUEST_PREFIX).append(method).append(" ")
.append(uri.toASCIIString()).append("\n");
}
private void printResponseLine(final StringBuilder b, final String note, final int status) {
b.append(NOTIFICATION_PREFIX)
.append(note)
.append(" on thread ").append(Thread.currentThread().getId())
.append(RESPONSE_PREFIX)
.append(Integer.toString(status))
.append("\n");
}
private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}
stream.mark(maxEntitySize + 1);
final byte[] entity = new byte[maxEntitySize + 1];
final int entitySize = stream.read(entity);
b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
if (entitySize > maxEntitySize) {
b.append("...more...");
}
b.append('\n');
stream.reset();
return stream;
}