In order to log all HTTP requests and response of Apache we need to implement HttpRequestInterceptor and HttpResponseInterceptor interfaces and then register them in the HTTP client.

Actually we just need to implement one method for each - process(..). That’s the plan, let’s do it.

LoggingRequestInterceptor

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.protocol.HttpContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;

public class LoggingRequestInterceptor implements HttpRequestInterceptor {

    @Override
    public void process(HttpRequest request, HttpContext context) throws IOException {
        String message = buildRequestEntry(request, context) 
                    + buildHeadersEntry(request.getAllHeaders())
                    + buildEntityEntry(request);
        MyLogger.info(message);
    }


    private String buildRequestEntry(HttpRequest request, HttpContext context) {
        return "\nRequest - "
                + request.getRequestLine().getMethod + " " 
                + context.getAttribute("http.target_host") 
                + request.getRequestLine().getUri();
    }

    private String buildHeadersEntry(Header[] headers) {
        return "\nHeaders: ["
                + Arrays.asList(headers).stream()
                        .map(header -> header.getName() + ": " + header.getValue())
                        .collect(Collectors.joining(", "))
                + "]";
    }

    private String buildEntityEntry(HttpRequest request) throws IOException {
        if (request instanceof HttpEntityEnclosingRequest) {
            HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            entity.writeTo(bs);
            return "\nPayload:\n" + new String(bs.toByteArray());
        }
    }
}

LoggingResponseInterceptor

Similarly for the HttpResponseInterceptor implementation

import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.entity.StringEntity;
import org.apache.http.protocol.HttpContext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;

public class LoggingResponseInterceptor implements HttpResponseInterceptor {

    @Override
    public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
        String message = buildStatusEntry(response)
                    + buildHeadersEntry(response.getAllHeaders())
                    + buildEntityEntry(response);
        MyLogger.info(message);
    }

    private String buildStatusEntry(HttpResponse response) {
        return "\nResponse - "
                + response.getStatusLine().getStatusCode() + " "
                + response.getStatusLine().getReasonPhrase();
    }

    private String buildHeadersEntry(Header[] headers) {
        return "\nHeaders: ["
                + Arrays.asList(headers).stream()
                        .map(header -> header.getName() + ": " + header.getValue())
                        .collect(Collectors.joining(", "))
                + "]";
    }

    private void buildEntityEntry() throws IOException {
        BufferedReader buffer = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        String payload = buffer.lines().collect(Collectors.joining("\n"));
        response.setEntity(new StringEntity(payload));
        return "\nPayload: \n" + payload;
    }

}

Here we had to do a little trick - we re-set response’s entity after reading. You can read it only once so if you don’t do that the reasponse will be unusable.

Register Logging Interceptors

Finally we register the logging interceptors in the Apache HTTP client.

CloseableHttpClient httpClient = HttpClientBuilder.create()
                .addInterceptorFirst(new LoggingRequestInterceptor())
                .addInterceptorLast(new LoggingResponseInterceptor())
                // More configuration if needed here
                .build();

That’s it.

Now you can use the httpClient as an http engine for RESTEasy or RestTemplate clients.

RESTEasy Proxy Configuration Example

UserService userServiceClient = new ResteasyClientBuilder()
                .httpEngine(new ApacheHttpClient43Engine(httpClient, true))
                .build()
                .target(serviceBaseUrl)
                .proxy(UserService.class);


You may also find these posts interesting: