J'essaie de consigner une demande à l'aide de Spring 5 WebClient. Avez-vous une idée de comment pourrais-je atteindre cet objectif?
(J'utilise Spring 5 et Spring Boot 2)
Le code ressemble à ceci pour le moment:
try {
return webClient.get().uri(url, urlParams).exchange().flatMap(response -> response.bodyToMono(Test.class))
.map(test -> xxx.set(test));
} catch (RestClientException e) {
log.error("Cannot get counter from opus", e);
throw e;
}
Vous pouvez facilement le faire en utilisant ExchangeFilterFunction
Ajoutez simplement le filtre personnalisé logRequest
lorsque vous créez votre WebClient
en utilisant WebClient.Builder
.
Voici l'exemple d'un tel filtre et comment l'ajouter au WebClient
.
@Slf4j
@Component
public class MyClient {
private final WebClient webClient;
// Create WebClient instance using builder.
// If you use spring-boot 2.0, the builder will be autoconfigured for you
// with the "prototype" scope, meaning each injection point will receive
// a newly cloned instance of the builder.
public MyClient(WebClient.Builder webClientBuilder) {
webClient = webClientBuilder // you can also just use WebClient.builder()
.baseUrl("https://httpbin.org")
.filter(logRequest()) // here is the magic
.build();
}
// Just example of sending request
public void send(String path) {
ClientResponse clientResponse = webClient
.get().uri(uriBuilder -> uriBuilder.path(path)
.queryParam("param", "value")
.build())
.exchange()
.block();
log.info("Response: {}", clientResponse.toEntity(String.class).block());
}
// This method returns filter function which will log request data
private static ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers().forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
return Mono.just(clientRequest);
});
}
}
Ensuite, appelez simplement myClient.send("get");
et les messages de journalisation devraient être là.
Exemple de sortie:
Request: GET https://httpbin.org/get?param=value
header1=value1
header2=value2
Vous n'avez pas nécessairement besoin de lancer votre propre enregistreur, reactor.ipc.netty.channel.ChannelOperationsHandler
le fait pour vous. Configurez simplement votre système de journalisation pour que cette classe se connecte au niveau DEBUG:
2017-11-23 12:52:04.562 DEBUG 41449 --- [ctor-http-nio-5] r.i.n.channel.ChannelOperationsHandler : [id: 0x9183d6da, L:/127.0.0.1:57681 - R:localhost/127.0.0.1:8000] Writing object DefaultFullHttpRequest(decodeResult: success, version: HTTP/1.1, content: UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 0))
GET /api/v1/watch/namespaces/default/events HTTP/1.1
user-agent: ReactorNetty/0.7.1.RELEASE
Host: localhost:8000
accept-encoding: gzip
Accept: application/json
content-length: 0
Une façon d’avoir moins de bogues est de ne pas écrire de code quand cela est possible.
Nov 2018 :
Avec spring-webflux:5.1.2.RELEASE
, ce qui précède ne fonctionne plus. Utilisez les éléments suivants à la place:
logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=DEBUG
...
2018-11-06 20:58:58.181 DEBUG 20300 --- [ main] o.s.w.r.f.client.ExchangeFunctions : [2026fbff] HTTP GET http://localhost:8080/stocks/search?symbol=AAPL
2018-11-06 20:58:58.451 DEBUG 20300 --- [ctor-http-nio-4] o.s.w.r.f.client.ExchangeFunctions : [2026fbff] Response 400 BAD_REQUEST
Pour consigner les en-têtes ou le corps du formulaire, définissez la valeur ci-dessus sur TRACE
level; Cependant, cela ne suffit pas:
ExchangeStrategies exchangeStrategies = ExchangeStrategies.withDefaults();
exchangeStrategies
.messageWriters().stream()
.filter(LoggingCodecSupport.class::isInstance)
.forEach(writer -> ((LoggingCodecSupport)writer).setEnableLoggingRequestDetails(true));
client = WebClient.builder()
.exchangeStrategies(exchangeStrategies)
mars 2019 :
En réponse à une question du commentaire qui demandait comment consigner le corps de la demande et du corps de la réponse, je ne sais pas si Spring a un tel enregistreur, mais WebClient
est construit sur Netty, permettant ainsi la journalisation du débogage pour le paquet reactor.ipc.netty
devrait fonctionner, avec this answer.
Si vous ne voulez pas connecter le corps, c'est très simple.
Ajoutez les éléments suivants au fichier application.properties:
logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE
spring.http.log-request-details=true
La deuxième ligne entraîne l’inclusion des en-têtes dans le journal.
Ajoutez les éléments suivants au fichier application.properties:
logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE
Au lieu de la deuxième ligne ci-dessus, vous devez déclarer une classe comme celle-ci:
@Configuration
static class LoggingCodecConfig {
@Bean
@Order(0)
public CodecCustomizer loggingCodecCustomizer() {
return (configurer) -> configurer.defaultCodecs()
.enableLoggingRequestDetails(true);
}
}
Gracieuseté de cette réponse de Brian Clozel
Vous pouvez demander à netty de consigner la demande/les réponses en lui demandant de procéder à une écoute électronique. Si vous créez votre WebClient Spring comme ceci, il active l’option d’écoute électronique.
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().wiretap(true)
))
.build()
et puis avoir votre configuration de journalisation:
logging.level.reactor.netty.http.client.HttpClient: DEBUG
cela enregistrera tout pour la demande/réponse (y compris les corps), mais le format n'est pas spécifique à HTTP, donc pas très lisible.