springloggingspring-webfluxreactive-programming

log All response and request spring webflux


I want to add logging to my project ,I use webFilter to add log . I want to log request headers (except authentication header) ,request methods and response code I add this code to my project:

@Component
@Slf4j
public class LogFilter implements WebFilter {


    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange,
                             WebFilterChain webFilterChain) {
        log.info("log :: requestId: {}, ip: {}, method: {},path :{}, headers: {}, response :{}",
                serverWebExchange.getRequest().getId(), serverWebExchange.getRequest().getRemoteAddress(),
                serverWebExchange.getRequest().getMethod(), serverWebExchange.getRequest().getPath(),
                serverWebExchange.getRequest().getHeaders().entrySet()
                        .stream().filter(stringListEntry -> !stringListEntry.getKey().equals("Authorization")).toList(),
                serverWebExchange.getResponse().getStatusCode);
        return webFilterChain.filter(serverWebExchange);
    } 

I have All headers and needed information to log but response code is Always 200 OK but some time response is 400 in result.


Solution

  • Your solution is incorrect because you used the logger before the filter and the filter will be triggered if anyone subscribed to it, so Mono/Flux flow chain will run later rather than the other code. Your controller hasn't run yet, so you're reading the response code prematurely. Please in the future use debug mode and check which call will be run first.

    In my solution (this isn't the best, just one example), I am waiting for the end of communication. It can be complete or error. So, I watch those signals and will log if one of them.

    One solution:

    @Component
    public class LogFilter implements WebFilter {
        Logger log = LoggerFactory.getLogger(LogFilter.class);
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            return chain.filter(exchange)
                .doOnEach(signal -> {
                    if (signal.isOnComplete() || signal.isOnError()) {
                        log.info(
                            "log :: requestId: {}, ip: {}, method: {},path :{}, headers: {}, response :{}",
                            exchange.getRequest().getId(), exchange.getRequest().getRemoteAddress(),
                            exchange.getRequest().getMethod(), exchange.getRequest().getPath(),
                            exchange.getRequest().getHeaders().entrySet()
                                    .stream().filter(stringListEntry -> !stringListEntry.getKey().equals("Authorization")).toList(),
                            exchange.getResponse().getStatusCode()
                        );
                    }
                });
        }
    }
    

    Update: If you use data in MDC, consider the follow:

    What Is a Good Pattern for Contextual Logging? (MDC)

    Or you can use my library which is has wrapped this pattern: GitHub