Jersey: Logging Request and Response Example

By default, Jersey uses JUL for logging. Learn to use LoggingFeature and LoggingFilter classes to enable the additional logs of request and response.

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 !!

Weekly Newsletter

Stay Up-to-Date with Our Weekly Updates. Right into Your Inbox.

Comments

Subscribe
Notify of
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.