RequestAccessLogFilter.java

package com.project.auth.config.web;

import com.project.auth.application.support.logging.LogSanitizer;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;

public class RequestAccessLogFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger("http.access");
    private static final String ACCESS_EVENT_TYPE = "HTTP_ACCESS";

    private final List<String> excludedPathPrefixes;

    public RequestAccessLogFilter(AccessLogProperties accessLogProperties) {
        this.excludedPathPrefixes = accessLogProperties.excludedPathPrefixes() == null
                ? List.of()
                : List.copyOf(accessLogProperties.excludedPathPrefixes());
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String path = request.getRequestURI();
        return excludedPathPrefixes.stream().anyMatch(path::startsWith);
    }

    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return true;
    }

    @Override
    protected boolean shouldNotFilterErrorDispatch() {
        return true;
    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        long startTime = System.nanoTime();

        try {
            filterChain.doFilter(request, response);
        } finally {
            long durationMs = (System.nanoTime() - startTime) / 1_000_000;
            logRequestSummary(request, response, durationMs);
        }
    }

    private void logRequestSummary(HttpServletRequest request, HttpServletResponse response, long durationMs) {
        String method = request.getMethod();
        String path = LogSanitizer.requestPath(request.getRequestURI());
        int status = response.getStatus();
        String remoteIp = LogSanitizer.clientIp(request.getRemoteAddr());
        String actorId = resolveActorId();
        String result = status < 400 ? "success" : "failure";

        log.atInfo()
                .addKeyValue("eventType", ACCESS_EVENT_TYPE)
                .addKeyValue("method", method)
                .addKeyValue("requestPath", path)
                .addKeyValue("status", status)
                .addKeyValue("durationMs", durationMs)
                .addKeyValue("remoteIp", remoteIp)
                .addKeyValue("actorId", actorId)
                .addKeyValue("result", result)
                .log("ACCESS");
    }

    private String resolveActorId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()
                && !"anonymousUser".equals(authentication.getPrincipal())) {
            return LogSanitizer.actorId(authentication.getName());
        }
        return LogSanitizer.actorId("anonymous");
    }
}