Java 9で実験的に導入された HttpClient はJava 11で安定しましたが、驚くことではありませんが、実際に使用しているプロジェクトはほとんどないようですドキュメントはほとんど存在しません。
HTTP呼び出しを行う際に最もよくある質問の1つは、要求/応答のロギングです。もちろん、HttpClient
を使用してそれをどのように行いますか?もちろん、すべての呼び出しで手動でログに記録しますか?他のすべてのHTTPクライアントによって提供されるようなインターセプターメカニズムはありますか?
jdk.internal.net.http.common.DebugLogger
ソースコード System.Logger
を使用します System.LoggerFinder
ロガーフレームワークを選択します。 JULがデフォルトの選択です。ロガー名は次のとおりです。
システムプロパティとして設定することで有効にできます。たとえば、-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
を指定すると、要求と応答をログに記録できます。
テスト/モッキングについては、オフラインテストを確認することをお勧めします。 http://hg.openjdk.Java.net/jdk/jdk/file/tip/test/jdk/Java/net/httpclient/offline /
達成しようとしていることに応じて、「DelegatingHttpClient」を使用して、要求と応答をインターセプトしてログに記録することもできます。
Java APIドキュメントの他に、 http://openjdk.Java.net/groups/net/httpclient/index.html にもいくつかの高レベルのドキュメントがあります。
追記:
jdk.httpclient.HttpClient.log
プロパティは実装固有のプロパティであり、その値はカンマ区切りのリストで、Javaコマンドラインで診断/デバッグのために次の値で構成できます)。
-Djdk.httpclient.HttpClient.log=
errors,requests,headers,
frames[:control:data:window:all],content,ssl,trace,channel
私たちの側では、-Djdk.internal.httpclient.debug
によって提供されるログを十分に読み取ることができませんでした。私たちが思いついた解決策は、呼び出しを傍受してロギングを提供できるデコレータでHttpClientをラップすることです。ここではどのように見えるか(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);
}
}
そして、これが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() {
}
}