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 !!
Thanks, This is very helpful.
responseContext.getEntity() returnsobject not html, any idea how to solve this.
the log file, where it would be generated??
Nowhere. Only in Cloud. Call It Magic.
The same error with Jersey 2.21.
When I wrote the example, 2.19 was latest available Jersey version.
what version of jersey are you using for this example?.
LoggerFilter is a final class, So we can not extend this filter for custom logging.. I am using the jersey version 2.21.