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 olderLoggingFilter
.
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
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