How I can log the payload of Feign client request, response and URL. do I have to Implement an Interceptor? Because my requirement is logging the request and response on a special table on the database.
Feign has out of box logging mechanism and it can be achieved through simple steps.
If you are using spring-cloud-starter-feign
Feign using Slf4jLogger
for logging.Feign logging documentation
As per doc, the below logging levels are available to configure,
NONE
- No logging (DEFAULT).BASIC
- Log only the request method and URL and the response status code and execution time.HEADERS
- Log the basic information along with request and response headers.FULL
- Log the headers, body, and metadata for both requests and responses.Injecting the Logger.Level
bean is enough.
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
OR
If you prefer using configuration properties to configured all @FeignClient
, you can create configuration properties with default feign name.
feign:
client:
config:
default:
loggerLevel: basic
If you are using 'io.github.openfeign:feign-core'
If you are constructing the Feign builder then you can mention logLevel(Level.BASIC)
as
Feign.builder()
.logger(new Slf4jLogger())
.logLevel(Level.BASIC)
.target(SomeFeignClient.class, url);
We have the flexibility to customize the logging message
The default feign request and response logging
Request logging
Resopnse logging
we can customize the feign request, response logging pattern by overriding Logger#logRequest
and Logger#logAndRebufferResponse
methods. In the following example, we have customized request logging pattern
log(configKey, "---> %s %s HTTP/1.1 (%s-byte body) ", request.httpMethod().name(), request.url(), bodyLength);
and response logging pattern
log(configKey, "<--- %s %s HTTP/1.1 %s (%sms) ", request.httpMethod().name(), request.url(), status, elapsedTime);
The Full example is
import feign.Logger;
import feign.Request;
import feign.Response;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import static feign.Logger.Level.HEADERS;
@Slf4j
public class CustomFeignRequestLogging extends Logger {
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
if (logLevel.ordinal() >= HEADERS.ordinal()) {
super.logRequest(configKey, logLevel, request);
} else {
int bodyLength = 0;
if (request.requestBody().asBytes() != null) {
bodyLength = request.requestBody().asBytes().length;
}
log(configKey, "---> %s %s HTTP/1.1 (%s-byte body) ", request.httpMethod().name(), request.url(), bodyLength);
}
}
@Override
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime)
throws IOException {
if (logLevel.ordinal() >= HEADERS.ordinal()) {
super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
} else {
int status = response.status();
Request request = response.request();
log(configKey, "<--- %s %s HTTP/1.1 %s (%sms) ", request.httpMethod().name(), request.url(), status, elapsedTime);
}
return response;
}
@Override
protected void log(String configKey, String format, Object... args) {
log.debug(format(configKey, format, args));
}
protected String format(String configKey, String format, Object... args) {
return String.format(methodTag(configKey) + format, args);
}
}
NOTE: Request payload can be easily logged through
String bodyText =
request.charset() != null ? new String(request.body(), request.charset()) : null;
but be careful writing the response payload after you are reading the input stream Util.toByteArray(response.body().asInputStream())
then you have to construct the response again like response.toBuilder().body(bodyData).build()
. Otherwise, you will end up with the expection. The reason is response streams are read and always closed before returning, thats why the method is named as logAndRebufferResponse
How to use the custom CustomFeignRequestLogging
?
If you are building feign client using just 'io.github.openfeign:feign-core'
Feign.builder()
.logger(new CustomFeignRequestLogging())
.logLevel(feign.Logger.Level.BASIC);
If you are using 'org.springframework.cloud:spring-cloud-starter-openfeign'
@Configuration
public class FeignLoggingConfiguration {
@Bean
public CustomFeignRequestLogging customFeignRequestLogging() {
return new CustomFeignRequestLogging();
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With