简体   繁体   English

如何使用 java.net.http.HttpClient 记录请求/响应?

[英]How to log request/response using java.net.http.HttpClient?

The HttpClient introduced experimentally in Java 9 is now stable in Java 11, but not surprisingly, very few projects seem to actually use it.在 Java 9 中实验性引入的HttpClient现在在 Java 11 中是稳定的,但毫不奇怪,似乎很少有项目实际使用它。 Documentation is almost non-existing.文档几乎不存在。

One of the most commons asks while making a HTTP call is logging of request/response.进行 HTTP 调用时最常见的问题之一是记录请求/响应。 How would you do that using the HttpClient , without of course, logging it manually in every single call?您将如何使用HttpClient做到这一点,当然,不用在每次调用中手动记录它? Is there an interceptor mechanism like that offered by all other HTTP clients?是否有类似所有其他 HTTP 客户端提供的拦截器机制?

You can log request and responses by specifying -Djdk.httpclient.HttpClient.log=requests on the Java command line.您可以通过在 Java 命令行上指定-Djdk.httpclient.HttpClient.log=requests来记录请求和响应。

As for testing/mocking you might want to have a look at the offline test: http://hg.openjdk.java.net/jdk/jdk/file/tip/test/jdk/java/net/httpclient/offline/至于测试/模拟,你可能想看看离线测试: http : //hg.openjdk.java.net/jdk/jdk/file/tip/test/jdk/java/net/httpclient/offline/

Depending on what you are looking to achieve you could use a "DelegatingHttpClient" to intercept and log requests and responses too.根据您要实现的目标,您也可以使用“DelegatingHttpClient”来拦截和记录请求和响应。

Besides the Java API documentation there's also some high level documentation at http://openjdk.java.net/groups/net/httpclient/index.html除了 Java API 文档之外,还有一些高级文档位于http://openjdk.java.net/groups/net/httpclient/index.html

Additional note:补充说明:

The jdk.httpclient.HttpClient.log property is an implementation specific property whose value is a comma separated list which can be configured on the Java command line for diagnosis/debugging purposes with the following values: jdk.httpclient.HttpClient.log属性是一个实现特定的属性,其值为逗号分隔的列表,可以在 Java 命令行上使用以下值进行配置以进行诊断/调试:

-Djdk.httpclient.HttpClient.log=
       errors,requests,headers,
       frames[:control:data:window:all],content,ssl,trace,channel,all

If we look at jdk.internal.net.http.common.DebugLogger source code we can see a few loggers using System.Logger , which in turn will use System.LoggerFinder to select the logger framework.如果我们查看jdk.internal.net.http.common.DebugLogger源代码,我们可以看到一些使用System.Logger记录器,这些记录器又会使用System.LoggerFinder来选择记录器框架。 JUL is the default choice. JUL 是默认选择。 The logger names are:记录器名称是:

  • jdk.internal.httpclient.debug jdk.internal.httpclient.debug
  • jdk.internal.httpclient.websocket.debug jdk.internal.httpclient.websocket.debug
  • jdk.internal.httpclient.hpack.debug jdk.internal.httpclient.hpack.debug

They can be enabled by setting them as a system property.可以通过将它们设置为系统属性来启用它们。 For example running with -Djdk.internal.httpclient.debug=true will produce:例如使用-Djdk.internal.httpclient.debug=true运行将产生:

DEBUG: [main] [147ms] HttpClientImpl(1) proxySelector is sun.net.spi.DefaultProxySelector@6dde5c8c (user-supplied=false)
DEBUG: [main] [183ms] HttpClientImpl(1) ClientImpl (async) send https://http2.github.io/ GET
DEBUG: [main] [189ms] Exchange establishing exchange for https://http2.github.io/ GET,
     proxy=null
DEBUG: [main] [227ms] PlainHttpConnection(?) Initial receive buffer size is: 43690
DEBUG: [main] [237ms] PlainHttpConnection(SocketTube(1)) registering connect event
DEBUG: [HttpClient-1-SelectorManager] [239ms] SelectorAttachment Registering jdk.internal.net.http.PlainHttpConnection$ConnectEvent@354bf356 for 8 (true)
...

On our side, we did not find the logging provided by -Djdk.internal.httpclient.debug readable enough.在我们这边,我们没有发现-Djdk.internal.httpclient.debug提供的日志足够可读。 The solution we came up with is to wrap the HttpClient with a decorator that will be able to intercept the calls and provide logging.我们提出的解决方案是用一个装饰器包装 HttpClient,该装饰器将能够拦截调用并提供日志记录。 Here how it somehow looks (should be done not only for send but sendAsync methods) :这是它的外观(不仅应该针对send而且应该针对sendAsync方法完成):

public class HttpClientLoggingDecorator extends HttpClient {

  private static final Logger logger = Logger.getLogger(HttpClientLoggingDecorator.class.getName());

  private final HttpClient client;

  ...

  @Override
  public <T> HttpResponse<T> send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler)
    throws IOException,
      InterruptedException
  {
    subscribeLoggerToRequest(req);

    HttpResponse<T> response = client.send(req, responseBodyHandler);

    logResponse(response);
    return response;
  }

  private void subscribeLoggerToRequest(HttpRequest req) {
    // define a consumer for how you want to log
    // Consumer<String> bodyConsumer = ...;
    if (req.bodyPublisher().isPresent()) {
      req.bodyPublisher()
              .ifPresent(bodyPublisher -> bodyPublisher.subscribe(new HttpBodySubscriber(bodyConsumer)));
    } else {
      bodyConsumer.accept(NO_REQUEST_BODY);
    }
  }

  private <T> void logResponse(HttpResponse<T> response) {
    // String responseLog = ...;
    logger.info(responseLog);
  }

}

And here is the HttpBodySubscriber :这是HttpBodySubscriber

public class HttpBodySubscriber implements Flow.Subscriber<ByteBuffer> {

  private static final long UNBOUNDED = Long.MAX_VALUE;

  private final Consumer<String> logger;

  public HttpBodySubscriber(Consumer<String> logger) {
    this.logger = logger;
  }

  @Override
  public void onSubscribe(Flow.Subscription subscription) {
    subscription.request(UNBOUNDED);
  }

  @Override
  public void onNext(ByteBuffer item) {
    logger.accept(new String(item.array(), StandardCharsets.UTF_8));
  }

  @Override
  public void onError(Throwable throwable) {
  }

  @Override
  public void onComplete() {
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM