7.4 Interceptors and API Logging Automation
In Spring, the HandlerInterceptor allows you to perform specific logic commonly by intercepting the request before and after the DispatcherServlet calls the Controller.
It is very frequently used for authentication (Authentication check), generic request/response format manipulation, and importantly, for writing API call logs regarding requests sent by clients.
HandlerInterceptor Interface
To implement an interceptor, inherit (or implement) HandlerInterceptor. It has three main methods.
preHandle(): Executed before entering the controller. (Most frequently used). If it returnsfalse, the method does not proceed to the next step (the controller).postHandle(): Executed after the controller execution, before view rendering. (Not very frequently used in REST APIs).afterCompletion(): Executed after all view rendering and response transmission tasks are completely finished. It is called whether it finished successfully or an exception occurred, so it is used for time measurement or final resource cleanup.
API Call Logging Interceptor Implementation Example
This is an interceptor that measures and logs the time a request came in, the URI, and the time taken after the request processing is finished.
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Slf4j
@Component
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// Temporarily store the arrival time in the request object's attribute.
request.setAttribute("startTime", System.currentTimeMillis());
log.info("[API Request] {} {}", request.getMethod(), request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
long startTime = (Long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
log.info("[API Response] {} {} - Processing time: {}ms, Response code: {}",
request.getMethod(), request.getRequestURI(), duration, response.getStatus());
if (ex != null) {
log.error("[API Error] Exception occurred: ", ex);
}
}
}
Registering the Interceptor
To make the created interceptor work, you need to register it in the Spring container's WebMvc configuration. Implement WebMvcConfigurer and override the addInterceptors method.
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final LoggingInterceptor loggingInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/api/**") // The path patterns we'll apply it to
.excludePathPatterns("/api/exclude"); // Patterns to exclude from log monitoring
}
}
By setting it up like this, even if you add hundreds of controllers later, all request and response processing under /api/ will be systematically logged without the developer having to manually insert System.out.println statements. These logs become invaluable resources for analyzing the causes of failures or monitoring the performance of slow APIs.