Previously, we’ve talked about making http calls through WebClient, and also how to test. Sometimes however, you’ll want information about the requests and responses in the actual usage, so that you can fix bugs in production.
In this article, we’ll cover intercepting filters that we can use to print such useful information.
Methods
Let’s first determine what we want from the interceptors. Having the full request may be a bit much at times, as it would produce way too much information. The headers will already give us plenty of information for any call that we could then afterwards reproduce.
// TODO: 26.06.22 replace println with logger
private fun logRequest(): ExchangeFilterFunction{
return ExchangeFilterFunction.ofRequestProcessor { clientRequest: ClientRequest ->
println("Request: ${clientRequest.method()} ${clientRequest.url()}")
clientRequest.headers()
.forEach { name: String?, values: List<String?> ->
values.forEach(
Consumer { value: String? ->
println(
"${name}=${value}"
)
})
}
Mono.just(clientRequest)
}
}
private fun logResponse(): ExchangeFilterFunction {
return ExchangeFilterFunction.ofResponseProcessor { clientResponse: ClientResponse ->
println("Response status: ${clientResponse.statusCode()}")
clientResponse.headers().asHttpHeaders()
.forEach { name: String?, values: List<String?> ->
values.forEach(
Consumer { value: String? ->
println(
"${name}=${value}"
)
})
}
Mono.just(clientResponse)
}
}
As you can see, these methods would take the headers, and print them. Simple as that, but already really powerful.
Inclusion in the WebClient
Now, to use the methods above, we need to add them to the WebClient
. The previous bean has now two additional lines.
@Bean
fun coinGeckoWebClient(@Value("\${service.coingecko.base-url}") baseUrl: String): WebClient {
return WebClient.builder()
.baseUrl(baseUrl)
.filter(logRequest()) // logging the request headers
.filter(logResponse()) // logging the response headers
.clientConnector(ReactorClientHttpConnector(httpClient()))
.codecs { configurer ->
configurer
.defaultCodecs()
.maxInMemorySize(16 * 1024 * 1024)
}
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, "${MediaType.APPLICATION_JSON}")
.defaultHeader(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString())
.build()
}
Usage
Now, let’s run the same call we’ve been running before and check out what the result is.
There you go! Now you have all the information, and you can read through your logs if you’ve received any 4XX
or 5XX
responses from any of the servers you’ve tried calling. You can make alerts based off this, or anything else you may fancy!
In a future article, I may decide to delve a bit deeper into logging the bodies as well. However, this is usually simply leading to bloated logs and should be used very sparingly, therefore I will need some time to decide whether I’ll even attempt it myself.