1

I'm creating a logging system where all the logs generated by specific request are stored in a request scoped object and subsequently logged on a ContainerResponseFilter. So instead of having many logs per request, I'll have just one with all the information regarding that specific request.

How I store the logs:

@RequestScoped
class Logs {
    private List<Log> logs;
    ...
}

How I add logs:

@Inject
Logs logs;

...

private void myFunction(){
    logs.add(new Log(...));
}

How I write them in a ContainerResponseFilter:

@Inject
Logs logs;

@Inject
Logger logger;

@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {
    logger.log(logs.entries());
}

How I launch the async code:

Uni.createFrom().item(myItem)
                .emitOn(Infrastructure.getDefaultWorkerPool())
                .subscribe().with(this::myFunc) 

The issue I'm having is with a request that launches an async Uni which runs on a worker thread. Since it's not on the same thread of the request, I have two problems:

  • The ContainerResponseFilter may finish executing before the Uni, which means that any logs performed in the Uni are lost.
  • Since the @RequestScoped context seems to be thread local, the logs performed in the Uni are placed on a completely separate structure.

By having the ContainerResponseFilter launch a Uni which runs after the previously mentioned async uni I managed to solve the first issue, but not the second.

Any suggestions on how to solve this last issue? I would be fine with just logging immediately and individually when running on a worker thread or any other context that is asynchronous to the request itself.

David Reis
  • 11
  • 2
  • 6
  • Can you post some (psedo)code of what you are trying to do? – geoand Apr 18 '23 at 06:40
  • @geoand added an example of how I'm launching the async code for better context – David Reis Apr 18 '23 at 09:17
  • Interesting... What are you actually trying to do in the `ContainerResponseFilter`? – geoand Apr 18 '23 at 09:35
  • @geoand I am trying to take all the logs that were performed during the request and actually log them to stdout. – David Reis Apr 18 '23 at 11:17
  • And is async being used? – geoand Apr 18 '23 at 11:18
  • @geoand The Uni runs asynchronously to the request itself due to the statement `.emitOn(Infrastructure.getDefaultWorkerPool())` – David Reis Apr 18 '23 at 13:04
  • I get that, I am asking why you need to do that – geoand Apr 18 '23 at 13:38
  • The reason is that I want a request to trigger some work that doesn't block the request. So the request returns immediately and does not wait for that work to finish. – David Reis Apr 19 '23 at 12:25
  • To have proper context propagation I used `quarkus-smallrye-context-propagation` as described [here](https://quarkus.io/guides/context-propagation). However, I still had some trouble with the `@RequestScoped` object being destroyed after the request ends, which is expected, so I just refactored the Uni to also work without the `@RequestScoped` object. – David Reis Apr 19 '23 at 12:39

1 Answers1

0

If you really need to have something done in an async manner (which I admit I am not sure you do from the description) in the filter, then you should use Quarkus's ServerResponseFilter which allows you to return Uni as a response type.

He is a dummy example:

public class UniResponseFilter {

    @ServerResponseFilter
    Uni<Void> filter() {
        return Uni.createFrom().nullItem();
    }
}

You can fine more information here.

geoand
  • 60,071
  • 24
  • 172
  • 190
  • I believe this would cause the request to wait for the Uni to finish right? Which is what I would like to avoid in the first place. I solved it using another approach which I've documented in a new answer below for anyone that comes across a similar issue. – David Reis Apr 19 '23 at 12:29
  • What exactly do you mean by wait? – geoand Apr 20 '23 at 07:26