Hvordan logger man anmodning/svar ved hjælp af java.net.http.HttpClient?
Du kan logge anmodninger og svar ved at angive -Djdk.httpclient.HttpClient.log=requests
på Java-kommandolinjen.
Hvad angår test/hån, vil du måske tage et kig på offline testen:http://hg.openjdk.java.net/jdk/jdk/file/tip/test/jdk/java/net/httpclient/offline/
Afhængigt af hvad du søger at opnå, kan du bruge en "DelegatingHttpClient" til også at opsnappe og logge anmodninger og svar.
Udover Java API-dokumentationen er der også noget højt niveau dokumentation på http://openjdk.java.net/groups/net/httpclient/index.html
Yderligere bemærkning:
jdk.httpclient.HttpClient.log
egenskab er en implementeringsspecifik egenskab, hvis værdi er en kommasepareret liste, som kan konfigureres på Java-kommandolinjen til diagnose-/fejlretningsformål med følgende værdier:
-Djdk.httpclient.HttpClient.log=
errors,requests,headers,
frames[:control:data:window:all],content,ssl,trace,channel,all
Hvis vi ser på jdk.internal.net.http.common.DebugLogger
kildekode kan vi se et par loggere ved hjælp af System.Logger
, som igen vil brugeSystem.LoggerFinder
for at vælge loggerramme. JUL er standardvalget. Loggerens navne er:
- jdk.internal.httpclient.debug
- jdk.internal.httpclient.websocket.debug
- jdk.internal.httpclient.hpack.debug
De kan aktiveres ved at indstille dem som en systemegenskab. For eksempel kører med -Djdk.internal.httpclient.debug=true
vil producere:
DEBUG: [main] [147ms] HttpClientImpl(1) proxySelector is [email protected] (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 [email protected] for 8 (true)
...
På vores side fandt vi ikke logningen leveret af -Djdk.internal.httpclient.debug
læsbar nok. Løsningen, vi fandt på, er at pakke HttpClienten med en dekorator, der vil være i stand til at opsnappe opkaldene og sørge for logning. Her hvordan det på en eller anden måde ser ud (bør ikke kun gøres for send
men sendAsync
metoder) :
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);
}
}
Og her er 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() {
}
}