I want to capture and log the response payload in JAX-RS filter. Here is the code snippet of the filter method that I'm using to intercept the response. (FYI - I'm using RestEasy for implementation)
@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws IOException {
...
final OutputStream out = responseContext.getEntityStream();
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
out.write(baos.toByteArray());
....
}
}
However, the ByteArrayOutputStream turns out be empty. Looking at the RestEasy code, it's using DeferredOutputStream, but not sure how it would matter here in pulling the response payload. I have tried writing to byte[] directly, but that doesn't help either. Am I missing anything here ? Thanks.
If you don't want to write more data to the response you don't need to deal with the OutputStream. Just use the response entity:
@Provider
public class SomeFilter implements ContainerResponseFilter {
private Logger LOG = LoggerFactory.getLogger(SomeFilter.class);
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
LOG.info("response entity: " + responseContext.getEntity());
}
}
The OutputStream is empty at the time the Filter is called because the JAX-RS runtime has not written to it. After your Filter the runtime will choose the correct MessageBodyWriter which will serialize the entity to the OutputStream.
You could also intercept all MessageBodyWriters with a WriterInterceptor. Following example passes a ByteArrayOutputStream to the MessageBodyWriter and restores the original OutputStream afterwards:
@Provider
public class ResponseInterceptor implements WriterInterceptor {
private Logger LOG = LoggerFactory.getLogger(ResponseInterceptor.class);
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
OutputStream originalStream = context.getOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
context.setOutputStream(baos);
try {
context.proceed();
} finally {
LOG.info("response body: " + baos.toString("UTF-8"));
baos.writeTo(originalStream);
baos.close();
context.setOutputStream(originalStream);
}
}
}
I had the same problem and solved differently, so I leave here my response as well although the question is already marked as correctly answered.
I implemented a ContainerResponseFilter
and injected Providers
through which I retrieved the MessageBodyWriter
for the particular entity of the response and the particular MediaType
; then I used it to write the entity to an accessible OutputStream
which I used to log the entity.
This approach allows you to capture the exact payload of the response, not just the entity attached to the Response
, i.e. if the entity is going to be serialized as JSON, then you will log the JSON, if it is serialized as XML, you will log the XML. If using the toString()
method of the attached entity is enough, this approach is just a useless computational cost.
Here is the code (the trick is done in the function CustomResponseLogger.payloadMessage
):
@Provider
public class CustomResponseLogger implements ContainerResponseFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomResponseLogger.class);
@Context private Providers providers;
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
String message = new String("Outgoing message").concat(System.lineSeparator());
if (responseContext.getMediaType() != null)
message = message.concat("Content-Type: ").concat(responseContext.getMediaType().toString()).concat(System.lineSeparator());
message = message.concat("Payload: ").concat(payloadMessage(responseContext)).concat(System.lineSeparator());
LOGGER.info(message);
}
private String payloadMessage(ContainerResponseContext responseContext) throws IOException {
String message = new String();
if (responseContext.hasEntity()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Class<?> entityClass = responseContext.getEntityClass();
Type entityType = responseContext.getEntityType();
Annotation[] entityAnnotations = responseContext.getEntityAnnotations();
MediaType mediaType = responseContext.getMediaType();
@SuppressWarnings("unchecked")
MessageBodyWriter<Object> bodyWriter = (MessageBodyWriter<Object>) providers.getMessageBodyWriter(entityClass,
entityType,
entityAnnotations,
mediaType); // I retrieve the bodywriter
bodyWriter.writeTo(responseContext.getEntity(),
entityClass,
entityType,
entityAnnotations,
mediaType,
responseContext.getHeaders(),
baos); // I use the bodywriter to write to an accessible outputStream
message = message.concat(new String(baos.toByteArray())); // I convert the stream to a String
}
return message;
}
}
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With