Jersey: Logging Request and Response Example

By default, Jersey uses JUL (Java Util Logging) for logging – and does not print request/response bodies in the logs. To print entity content, we must create the org.glassfish.jersey.logging.LoggingFeature extension. This feature enables logging requests and/or responses on client-side and/or server-side.

LoggingFeature has been introduced in Jersey 2.23 version and deprecates an older LoggingFilter.

1. Enabling LoggingFeature with Properties [Since Jersey 2.23]

One of the straightforward methods to configure the JUL logging to output the request and responses in more detail is configuring the logging properties in the Client.

Client client = ClientBuilder.newBuilder()
  .register(LoggingFeature.class)
  .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT, LoggingFeature.Verbosity.PAYLOAD_ANY)
  .property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT, Level.INFO.getName())
  .build();

There are plenty of other properties you can configure for more fine-grained control over the logging output. Find them in the LoggingFeature.java file.

2. Demo

Now when we send a request using the Jersey client, we can see the extended logging details for the request and response.

Consider the following API:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllItems(@CookieParam(value="cookieParam1") String cookieParam1,
          @CookieParam(value="cookieParam2") String cookieParam2) {

	System.out.println("cookieParam1 is :: " + cookieParam1);
	System.out.println("cookieParam2 is :: " + cookieParam2);

	List items = List.of(new Item(1L, "Item1"), new Item(2L, "Item2"));

	return Response.ok().entity(items)
		.cookie(new NewCookie("key1", "value1"))
		.cookie(new NewCookie("key2", "value2"))
		.build();
}

When we invoke this API, we get the additional logs in the console.

cookieParam1 is :: cookieValue1
cookieParam2 is :: cookieValue2

Apr 22, 2024 2:04:46 PM org.glassfish.jersey.logging.LoggingInterceptor log
INFO: 1 * Client response received on thread main
1 < 200
1 < Content-Type: application/json
1 < Date: Mon, 22 Apr 2024 08:34:46 GMT
1 < Server: Jetty(12.0.7)
1 < Set-Cookie: key1=value1;Version=1,key2=value2;Version=1
1 < Transfer-Encoding: chunked
[{"id":1,"name":"Item1"},{"id":2,"name":"Item2"}]

3. Creating Custom LoggingFilter [Before Jersey 2.23]

Let’s go straight to the custom logging filter class for this jersey demo.

package com.howtodoinjava.jersey.provider;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
 
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
 
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.message.internal.ReaderWriter;
 
public class CustomLoggingFilter extends LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter 
{
  @Override
  public void filter(ContainerRequestContext requestContext)  throws IOException 
  {
    StringBuilder sb = new StringBuilder();
    sb.append("User: ").append(requestContext.getSecurityContext().getUserPrincipal() == null ? "unknown"
            : requestContext.getSecurityContext().getUserPrincipal());
    sb.append(" - Path: ").append(requestContext.getUriInfo().getPath());
    sb.append(" - Header: ").append(requestContext.getHeaders());
    sb.append(" - Entity: ").append(getEntityBody(requestContext));
    System.out.println("HTTP REQUEST : " + sb.toString());
  }
 
  private String getEntityBody(ContainerRequestContext requestContext) 
  {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    InputStream in = requestContext.getEntityStream();
     
    final StringBuilder b = new StringBuilder();
    try
    {
      ReaderWriter.writeTo(in, out);
 
      byte[] requestEntity = out.toByteArray();
      if (requestEntity.length == 0)
      {
        b.append("").append("\n");
      }
      else
      {
        b.append(new String(requestEntity)).append("\n");
      }
      requestContext.setEntityStream( new ByteArrayInputStream(requestEntity) );
 
    } catch (IOException ex) {
      //Handle logging error
    }
    return b.toString();
  }
 
  @Override
  public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException 
  {
    StringBuilder sb = new StringBuilder();
    sb.append("Header: ").append(responseContext.getHeaders());
    sb.append(" - Entity: ").append(responseContext.getEntity());
    System.out.println("HTTP RESPONSE : " + sb.toString());
  }
}

To register this CustomLoggingFilter, register in this way.

package com.howtodoinjava.jersey;
 
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
 
import com.howtodoinjava.jersey.provider.CustomLoggingFilter;
 
public class CustomApplication extends ResourceConfig 
{
  public CustomApplication() 
  {
    packages("com.howtodoinjava.jersey");
    register(JacksonFeature.class);
 
    register(CustomLoggingFilter.class);
  }
}

Now if you try to use any existing REST API without CustomLoggingFilter, logs will appear in this way.

Request

Jersey-custom-logging

Logs

Sep 30, 2015 6:18:41 PM org.glassfish.jersey.filter.LoggingFilter log
INFO: 1 * Server has received a request on thread http-bio-8080-exec-4
1 > POST http://localhost:8080/JerseyDemos/rest/employees
1 > accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
1 > accept-encoding: gzip, deflate
1 > accept-language: null
1 > cache-control: no-cache
1 > connection: keep-alive
1 > content-length: 35
1 > content-type: application/json; charset=UTF-8
1 > host: localhost:8080
1 > pragma: no-cache
1 > user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0

Sep 30, 2015 6:18:41 PM org.glassfish.jersey.filter.LoggingFilter log
INFO: 1 * Server responded with a response on thread http-bio-8080-exec-4
1 < 200
1 < Content-Type: application/json

After adding CustomLoggingFilter, you will get much better logs.

Please feel free to add/remove information from log statements as per your need. There is plenty of other useful information you may add in these logs.

Happy Learning !!

Comments

Subscribe
Notify of
guest
7 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode