Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用java.net.http.HttpClient记录请求/响应?_Java_Logging_Java 9_Java 11_Java Http Client - Fatal编程技术网

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

如何使用java.net.http.HttpClient记录请求/响应?,java,logging,java-9,java-11,java-http-client,Java,Logging,Java 9,Java 11,Java Http Client,在Java9中实验性地引入的方法现在在Java11中是稳定的,但毫不奇怪,很少有项目实际使用它。文件几乎不存在 在进行HTTP调用时,最常见的问题之一是记录请求/响应。您将如何使用HttpClient,而不用在每次调用中手动记录它?是否有像所有其他HTTP客户机提供的拦截机制?如果我们查看jdk.internal.net.HTTP.common.DebugLogger源代码,我们可以看到一些记录器使用,这些记录器反过来将用于选择记录器框架。JUL是默认选择。记录器名称为: jdk.intern

在Java9中实验性地引入的方法现在在Java11中是稳定的,但毫不奇怪,很少有项目实际使用它。文件几乎不存在


在进行HTTP调用时,最常见的问题之一是记录请求/响应。您将如何使用
HttpClient
,而不用在每次调用中手动记录它?是否有像所有其他HTTP客户机提供的拦截机制?

如果我们查看jdk.internal.net.HTTP.common.DebugLogger源代码,我们可以看到一些记录器使用,这些记录器反过来将用于选择记录器框架。JUL是默认选择。记录器名称为:

  • jdk.internal.httpclient.debug
  • jdk.internal.httpclient.websocket.debug
  • jdk.internal.httpclient.hpack.debug
可以通过将它们设置为系统属性来启用它们。例如,使用
-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)
...

您可以通过在Java命令行上指定
-Djdk.httpclient.httpclient.log=requests
来记录请求和响应

至于测试/模拟,您可能想看看离线测试:

根据您希望实现的目标,您也可以使用“DelegatingHttpClient”来拦截和记录请求和响应

除了JavaAPI文档之外,还有一些高级文档

补充说明:

jdk.httpclient.httpclient.log
属性是一个特定于实现的属性,其值是一个逗号分隔的列表,可以在Java命令行上配置该属性,以便使用以下值进行诊断/调试:

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

在我们这边,我们发现
-Djdk.internal.httpclient.debug提供的日志不够可读。我们提出的解决方案是用一个能够拦截调用并提供日志记录的装饰器包装HttpClient。下面是它的外观(不仅应该针对
send
方法,而且应该针对
sendAsync
方法):


那个API看起来像一场灾难。它甚至没有可模仿的界面。(更不用说,正如您所观察到的,HTTP客户端20年来一直标准化的、支持特殊情况的顶级接口的、明显缺乏任何通用的拦截器机制。)很难同意“文档几乎不存在”。首先,有一个很好的javadoc,里面有很多示例。第二,youtube上有很多网络广播,都是由创建它的人制作的,比如说@pavel,你看到的肯定不是javadoc;如果这是“好的”,你的期望很低。而网络广播是良好文档的薄弱借口。@AbhijitSarkar,你可能把javadoc误认为是教程/指南/手册。请定义文档。@pavel“有用的”,也就是“除了hello world之外的其他东西”怎么样?我也在看这个。它用朱尔吗?在这种情况下,我可以将它连接到SLF4J,这取决于Javadoc中的“当java.logging模块存在时,系统默认的LoggerFinder实现使用java.util.logging作为后端框架”<代码>java--列出模块| grep日志
显示
java。logging@11.0.1
,所以我想,我的问题的答案是“是的,它使用JUL,除非你弄乱它”。)我最终切换到OkHttp,因为日志太冗长而没有用处。我接受你的回答,因为它适用于我的问题。希望客户端设计能够方便地插入拦截器-在调试时记录所有内容或什么都不记录似乎很愚蠢,-Djdk.internal.httpclient.debug记录器主要面向修复httpclient实现中的错误的JDK开发人员,而不是API用户。我不会推荐其他人使用它-Djdk.httpclient.httpclient.log=错误、请求、标题、帧[:控制:数据:窗口:所有..]、内容、ssl、跟踪、通道可能更适合API用户。jdk.httpclient.httpclient.log的可能值是什么?你是怎么发现那个论点的,有没有记录在案?请在回答中详细说明。附加说明设置了错误的属性,应该是-Djdk.httpclient.httpclient.log=错误、请求、标题、帧[:control:data:window:all]、内容、ssl、跟踪、通道似乎没有记录请求/响应主体。我猜如果是GET请求,URL将包含所有参数,但对于其他人来说,这是非常无用的。干得好,甲骨文!jdk.httpclient.httpclient.log的可能值在类jdk.httpclient.httpclient.log.javaI中的jdk11源代码中提到过,类似这样,但并不完全正确。您需要通过
onNext
捕获所有传入项目,并只在
onComplete
中记录它们。我很好奇为什么它不完全正确。通过上面提供的代码,在我看来,每次有数据进来时,我们都会记录日志。另外,如果我们只在完成时登录,这是否意味着我们不会在出错时登录?我的印象是,这在很大程度上取决于日志记录用例,所有这些解决方案都可能有意义。编辑:打字错误我应该明确我的观点,我的错。我们使用的是多部分消息,在这种情况下,
onNext
将针对同一请求被多次调用。最终的整个请求可以在
onComplete
上看到。我的理解是一样的,是的。谢谢你把它说清楚!
-Djdk.internal.httpclient.debug
记录器主要面向修复httpclient实现中的bug的JDK开发人员,而不是API用户。我不会推荐其他人使用它
-Djdk.httpclient.httpclient.log=错误、请求、标题、帧[:控件:数据:窗口:全部..],内容、ssl、跟踪、频道
可能更适合API用户。
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);
  }

}
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() {
  }

}