首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Dropwizard 1.0.2中使用LoggingFeature打印服务器响应?

如何在Dropwizard 1.0.2中使用LoggingFeature打印服务器响应?
EN

Stack Overflow用户
提问于 2016-10-21 07:59:08
回答 5查看 10.1K关注 0票数 4

以下代码将在Dropwizard 0.9.2和1.0.2中打印JSON服务器响应:

代码语言:javascript
复制
return ClientBuilder
        .newBuilder()
        .build()
        .register(new LoggingFilter(Logger.getLogger(LoggingFilter.class.getName()), true))

例如:

代码语言:javascript
复制
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

但是,在1.0.2中不推荐使用LoggingFilter,建议使用LoggingFeature。在LoggingFeature文档中,它说默认的详细内容是LoggingFeature.Verbosity.PAYLOAD_TEXT,所以我希望下面的代码仍然在Dropwizard 1.0.2中打印JSON服务器响应:

代码语言:javascript
复制
return ClientBuilder
        .newBuilder()
        .build()
        .register(new LoggingFeature(Logger.getLogger(getClass().getName())))

相反,日志只包含以下内容:

代码语言:javascript
复制
javax.ws.rs.NotAuthorizedException: HTTP 401 Unauthorized
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2016-10-21 13:15:40

这就是诀窍:

代码语言:javascript
复制
new LoggingFeature(Logger.getLogger(getClass().getName()), Level.OFF, LoggingFeature.Verbosity.PAYLOAD_TEXT, 8192)

我猜客户机中的日志功能就像一个过滤器,而不是像预期的那样作为一个包含。

票数 7
EN

Stack Overflow用户

发布于 2017-11-06 23:20:01

举一个简单的例子来说明一个常见的问题,它会让开发人员认为日志功能不起作用。

代码语言:javascript
复制
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或更高级别的所有日志消息(警告,严重,.),但它将忽略所有级别较低的消息,比如罚款。(它仍然将消息传递给它的父记录器,如果有)

注意:处理程序的默认日志级别是Level.ALL,它们不应该拒绝任何日志记录,只要您不修改它们的级别。

因此,您需要提高LoggingFeatures级别或降低记录器级别来查看消息。

解决方案1:提高LoggingFeature的级别:

代码语言:javascript
复制
new LoggingFeature(LOG, Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ALL, 8192)

解决方案2:降低日志的级别:

代码语言:javascript
复制
LOG.setLevel(Level.FINE)
票数 6
EN

Stack Overflow用户

发布于 2018-07-20 12:56:46

要使用LoggingFeature记录服务器和客户端请求/响应的自定义日志,您需要创建一个扩展LoggingFeature并实现ContainerRequestFilter、ContainerResponseFilter、ClientRequestFilter、ClientResponseFilter和WriterInterceptor的自定义类。

您需要重写LoggingFeature方法public boolean configure(FeatureContext context)并将CustomLoggingFeature对象注册到上下文中。

代码语言:javascript
复制
@Override
public boolean configure(FeatureContext context) {
    context.register(this);
    return true;
}

ContainerRequestFilter和ContainerResponseFilter接口有用于记录服务器请求和响应的方法。

ClientRequestFilter和ContainerResponseFilter接口有用于记录客户端请求和响应的方法。

另外,您需要实现WriterInterceptor来记录客户端请求和服务器响应主体。

最后在泽西注册了你的CustomLoggingFeature课程。

代码语言:javascript
复制
environment.jersey().register(new CustomLoggingFeature(Logger.getLogger(getClass().getName()), Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 8192));

我使用的是Dropwizard v1.3.5

这是我的实现。

代码语言:javascript
复制
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;
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40171273

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档