How do I log request and response details using Java 11 HttpClient?

If you are using Java 11’s HttpClient and want to log request and response details, you can achieve this by implementing a custom utility. Java 11’s HttpClient provides flexibility to log and inspect both requests and responses using its APIs. Here’s how you can log them:

Full Implementation for Logging Request and Response

Below is an example that demonstrates logging details such as HTTP headers, request body, response status, response headers, and response body.

package org.kodejava.net.http;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class HttpClientLogger {

   public static void main(String[] args) {
      try {
         // Create a sample HTTP GET request
         HttpClient httpClient = HttpClient.newHttpClient();

         HttpRequest request = HttpRequest.newBuilder()
                 .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
                 .GET()
                 .header("Accept", "application/json")
                 .timeout(Duration.ofSeconds(10))
                 .build();

         // Log Request Details
         logRequestDetails(request);

         // Send the request and receive a response
         HttpResponse<String> response = httpClient.send(request,
                 HttpResponse.BodyHandlers.ofString());

         // Log Response Details
         logResponseDetails(response);

      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   private static void logRequestDetails(HttpRequest request) {
      System.out.println("---- HTTP Request Details ----");
      System.out.println("Method: " + request.method());
      System.out.println("URI: " + request.uri());
      System.out.println("Headers: " + request.headers().map());
      request.bodyPublisher().ifPresentOrElse(
              bodyPublisher -> {
                 System.out.println("Request Body: (BodyPublisher details not directly accessible, consider passing it explicitly)");
              },
              () -> System.out.println("Request Body: No body")
      );
      System.out.println("-------------------------------");
   }

   private static void logResponseDetails(HttpResponse<String> response) {
      System.out.println("---- HTTP Response Details ----");
      System.out.println("Status Code: " + response.statusCode());
      System.out.println("Headers: " + response.headers().map());
      System.out.println("Body: " + response.body());
      System.out.println("--------------------------------");
   }
}

Explanation of the Code

  1. Setting up HttpClient:
    • We create a HttpClient instance using HttpClient.newHttpClient().
  2. Building the HttpRequest:
    • Use the HttpRequest.Builder to construct the request. This includes setting the URI, method, headers, and a timeout.
  3. Logging HTTP Request Details:
    • Log details of the request by accessing:
      • HTTP Method: HttpRequest::method
      • URI: HttpRequest::uri
      • Headers: HttpRequest::headers
      • The request body can be logged if you manually supply it during the request creation since BodyPublisher doesn’t provide a content preview.
  4. Sending the Request:
    • Use HttpClient::send to perform the HTTP operation, and specify HttpResponse.BodyHandlers.ofString() to read the response as a string.
  5. Logging HTTP Response Details:
    • Log response information:
      • Status Code: HttpResponse::statusCode
      • Headers: HttpResponse::headers
      • Body: HttpResponse::body

Notes

  1. POST/PUT Requests with a Body:
    • If you are sending POST or PUT requests that include a body (e.g., JSON or a form), you should explicitly log the body content when building the request. Example:
      HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
            .POST(HttpRequest.BodyPublishers.ofString("{\"key\":\"value\"}"))
            .header("Content-Type", "application/json")
            .build();
      

      To log the request body, simply store it in a separate variable and print it in logRequestDetails.

  2. Production Logging:

    • Avoid directly printing details to the console in production.
    • Use proper logging libraries like SLF4J with an implementation (e.g., Logback or Log4j) to write logs at different levels like DEBUG, INFO, ERROR, etc.
  3. Sensitive Data:
    • Avoid logging sensitive details like authentication headers or personal data (e.g., Authorization tokens, passwords).

This approach provides a reusable template for logging HTTP requests and responses while using the Java 11+ HttpClient.

How do I log from a servlet using ServletContext.log()?

In Java servlets, you can use the ServletContext.log() method to log messages, exceptions, or context-specific information. The ServletContext object is available in your servlet and allows you to log messages to the server’s log file.

Here are the common ways you can log using ServletContext.log():

1. Logging a simple message:

You can log plain text messages with ServletContext.log(String message).

package org.kodejava.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletContext;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/logMessage")
public class MyLogServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ServletContext context = getServletContext();
        context.log("This is a simple log message.");
        response.getWriter().println("Message logged.");
    }
}

2. Logging a message with an exception:

If you want to log an exception with additional context, you can use ServletContext.log(String message, Throwable throwable).

package org.kodejava.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletContext;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/logException")
public class MyLogServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ServletContext context = getServletContext();
        try {
            // Simulate an exception
            int result = 10 / 0;
        } catch (Exception e) {
            context.log("An exception occurred: ", e);
        }
        response.getWriter().println("Exception logged.");
    }
}

3. Including dynamic information:

You can include dynamic content in the log messages to make your logs more informative.

package org.kodejava.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletContext;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/logAccess")
public class MyLogServlet3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ServletContext context = getServletContext();
        String userIP = request.getRemoteAddr();
        context.log("Access from IP: " + userIP);
        response.getWriter().println("Access logged.");
    }
}

Points to Remember

  1. Where the logs are written: The ServletContext.log() messages are typically written to the application server’s log file (e.g., catalina.out for Tomcat). This location depends on the server configuration.
  2. Severity levels: The ServletContext.log() does not natively support different log levels like INFO, WARN, or ERROR. If you need more advanced logging capabilities, consider using a logging framework such as SLF4J, Log4j, or java.util.logging.
  3. Thread Safety: The logging methods of ServletContext are thread-safe.

This is how you can log using ServletContext.log() in your servlet-based application.

Maven dependencies

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

Maven Central