-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Logging Request Entity Body with Quarkus Resteasy Reactive #17280
Comments
/cc @FroMage, @geoand, @stuartwdouglas |
I am changing this to a feature request as the JAX-RS way of reading the entire body is definitely blocking, but it does make sense to provide an alternative that will allow users to read the body in request filters |
Thank you! |
👍🏼 |
I was thinking about this and I actually would like to know more about the use case. |
Our main use case is business related, having logs for the request and response of API calls enables the support team to help customers and integrators identify issues more easily and also makes the integration between different services implemented by different teams easier. |
So what kind of information do these logs contain? Just generic HTTP related info? |
They contain the endpoint, info about the caller, path and query parameters, the request body and an identifier for each request. For the response the contain the response body and the identifier. |
I can't shake the feeling that this is something that we could do ourselves and not expose. @FroMage @stuartwdouglas WDYT? |
I'm not sure, I guess it depends on how filters and reader interceptors interact. I don't think we want to invoke reader interceptors while in the middie of the first filter. |
+1 |
2 similar comments
+1 |
+1 |
I think the current solution would be to inject the |
Can't we just use io.vertx.core.Future processing to resolve the initial issue? So, in @KyriacosP's example it's possible to add something like
|
+1 |
1 similar comment
+1 |
Also needed. Trying to inject |
PvlPech's answer works perfectly, this is what we have in our repo. Might be worth making a FAQ/documentation entry on. import io.vertx.core.http.HttpServerRequest
import org.jboss.logging.Logger
import org.jboss.resteasy.reactive.server.ServerRequestFilter
import javax.inject.Inject
import javax.ws.rs.container.ContainerRequestContext
import javax.ws.rs.core.UriInfo
class Filters {
@Inject
private lateinit var logger: Logger
@ServerRequestFilter(priority = 0)
fun logBodyFilter(info: UriInfo, request: HttpServerRequest, ctx: ContainerRequestContext) {
request.body { buffer ->
logger.debug("Request body: ${buffer.result()}")
}
}
} |
@GavinRay97 I tried this solution but my body handler is never called (your logger in the example) My code:
|
Update: I found the issue. Apparently if you are not consuming the body in your rest controller, handler is never invoked. Not sure if this is a bug or a feature. But nice to know. |
I use Future bufferFuture = request.body(); |
@stuartwdouglas can you please mention how we can inject ServerHttpRequest? I get Edit: Never mind, I injected it using |
Confirming that this feature is needed on our project. |
We also need this feature 👍 |
the @GavinRay97 and @PvlPech workaround has also worked form me. Thanks guys. The only issue is that the
Update: |
Hello guys. I have very similar case but when it comes to my code: I need to obtain request body right after any exception is thrown so in result I'll have logged out only request bodies from failures. What I've tried so far is using ContainerRequestContext like this:
but the problem is that at this point request body is already consumed by JAX-RS so in result log always shows " Size of entity stream: 0" Then I tried some workaround and even tho it works as expected, it has some vulnerabilities:
then I can easily read requestBody from the map wherever I want but still, it doesn't look like the most optimal solution. Imagine somebody sends 2 requests with identical ID in header but different requestBody -> there is non-0 chance that in my map key-value will be replaced and in result logged requestBody won't match for given request. Also it requires alocating some memories for my map... Do you have any tips/solutions I can try to log request body only when response is failure (any exception thrown)? I'd be glad for any help. Thanks in advance. |
@geoand didn't you work on logging http requests in RR in the past? I can't find mention of it in https://quarkus.io/guides/resteasy-reactive |
I don't think we ever came to a good solution |
#22213 was the issue. Would this sort of logging solve your use-cases? I'm asking all the +1 people. |
+1 |
Hello Guys, I’ve implemented the following to log the request body. Could you guys please check it, if it meets the requirements or helps ? @ServerRequestFilter
public Uni<Void> filter(ResteasyReactiveContainerRequestContext requestContext) {
return readRequestBody(requestContext)
.onItem().invoke(body -> log.debug("Request body - {} received in filter for uri - {}", body, requestContext.getUriInfo().getRequestUri()))
.replaceWithVoid();
}
private Uni<String> readRequestBody(ResteasyReactiveContainerRequestContext context) {
if (isMultiPartRequest(context)) {
return Uni.createFrom().item(""); // Return empty string for multipart requests
}
return Uni.createFrom().item(() -> {
// Blocking code to read the request body
try {
byte[] body = context.getEntityStream().readAllBytes();
context.setEntityStream(new ByteArrayInputStream(body)); // Reset stream
return new String(body, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
})
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool()); // Offload to worker thread
}
private boolean isMultiPartRequest(ResteasyReactiveContainerRequestContext context) {
MediaType mediaType = context.getMediaType();
return mediaType != null && mediaType.isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE);
}
|
That's not the same at all as #22213 but if it works for you that's great :) |
I believe that we have found a solution for this, which works under all circumstances. The solution for us seemed to be the usage of
The following is a snippet / pseudocode. The concept is to catch the body before it is being read by / sent to the client, read the streams, and replace them with a new one so that the context can continue like before. public class BodyLoggerInterceprot implements ReaderInterceptor, WriterInterceptor {
private final MyLogger logger;
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
//retrieve the logging body
var originalStream = context.getOutputStream();
try (var baos = new LoggingBufferOutputStream(originalStream)) {
context.setOutputStream(baos);
context.proceed();
var output = baos.toByteArray();
logTransaction.setPayload(new String(output, StandardCharsets.UTF_8)); // <-- this is the body!
} catch (Exception e) {
//not sure about that, we need to check
context.setOutputStream(originalStream);
}
}
@Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
InputStream originalInputStream = context.getInputStream();
// Convert InputStream to String
String body = Streams.toString(originalInputStream); // <-- This is the line that reads the body!
// Reset the stream so it can be read again in the actual processing
context.setInputStream(new ByteArrayInputStream(body.getBytes()));
// Proceed with the rest of the interceptor chain
}
} Two notes:Usage of
|
Thanks for sharing @csotiriou! |
Describe the bug
I'm trying to implement a logging filter to log the request and response to the API endpoints of my Quarkus application. I'm using Quarkus 1.13.3.Final and quarkus-resteasy-reactive. I have a problem trying to log the request body when calling a non blocking endpoint. This is the code I'm using to log the request:
Expected behavior
Log the request body for both blocking and non blocking endpoints
Actual behavior
This works fine when I'm calling an API endpoint that has the @Blocking annotations but when I call a non blocking API I get the following error:
Is this the expected behavior or is this a bug? If this is the expected behavior for non blocking endpoints is there a way to log the incoming request body (without going into every individual endpoint and logging the body) ?
Output of
java -version
Java version: 11.0.9, vendor: Oracle Corporation,
Quarkus version or git rev
Quarkus 1.13.3.Final
Build tool (ie. output of
mvnw --version
orgradlew --version
)Apache Maven 3.6.3
The text was updated successfully, but these errors were encountered: