Implementing Request Logging with Spring AOP: A Practical Guide
This article demonstrates how to use Spring AOP to create a request‑logging aspect that captures request parameters, response data, execution time, and error information, while also addressing high‑concurrency logging issues and integrating trace‑ID tracking for easier debugging.
When a project reaches the integration testing phase, mismatched request parameters often cause interface failures; a request‑logging aspect can record the full call chain, providing evidence for troubleshooting.
Aspect‑Oriented Programming Overview
AOP separates core business logic from cross‑cutting concerns such as transaction management, security, caching, and logging, improving code readability and maintainability.
Define the Pointcut
@Pointcut("execution(* your_package.controller..*(..))")
public void requestServer() {}The pointcut targets all methods in the controller package.
Before Advice – Log Request Metadata
@Before("requestServer()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
LOGGER.info("===============================Start========================");
LOGGER.info("IP : {}", request.getRemoteAddr());
LOGGER.info("URL : {}", request.getRequestURL().toString());
LOGGER.info("HTTP Method : {}", request.getMethod());
LOGGER.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
}Logs IP, URL, HTTP method and the invoked controller method before execution.
Around Advice – Capture Parameters, Result and Duration
@Around("requestServer()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
LOGGER.info("Request Params : {}", getRequestParams(proceedingJoinPoint));
LOGGER.info("Result : {}", result);
LOGGER.info("Time Cost : {} ms", System.currentTimeMillis() - start);
return result;
}The around advice records input parameters, the method’s return value, and execution time.
After Advice – Mark End of Request
@After("requestServer()")
public void doAfter(JoinPoint joinPoint) {
LOGGER.info("===============================End========================");
}Simply logs a separator indicating the request has finished.
High‑Concurrency Optimization
To avoid interleaved log lines under heavy load, the article introduces a RequestInfo DTO that aggregates all data and logs it as a single JSON string.
@Data
public class RequestInfo {
private String ip;
private String url;
private String httpMethod;
private String classMethod;
private Object requestParams;
private Object result;
private Long timeCost;
}The around advice now populates this object and logs JSON.toJSONString(requestInfo), producing a compact, parseable line.
Error Logging with @AfterThrowing
@AfterThrowing(pointcut = "requestServer()", throwing = "e")
public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
RequestErrorInfo errorInfo = new RequestErrorInfo();
errorInfo.setIp(request.getRemoteAddr());
errorInfo.setUrl(request.getRequestURL().toString());
errorInfo.setHttpMethod(request.getMethod());
errorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()));
errorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
errorInfo.setException(e);
LOGGER.info("Error Request Info : {}", JSON.toJSONString(errorInfo));
}Logs request details together with the thrown exception without recording duration.
Trace‑ID Integration for Distributed Tracing
An interceptor adds a unique traceId to the logging context before each request and removes it after completion.
public class LogInterceptor implements HandlerInterceptor {
private static final String TRACE_ID = "traceId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
ThreadContext.put("traceId", traceId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ThreadContext.remove(TRACE_ID);
}
}The logging pattern is updated to include %X{traceId}, enabling end‑to‑end request tracing.
Complete Aspect Code
The article concludes with the full source of RequestLogAspect, which combines pointcut definition, before, around, after, and after‑throwing advices, as well as helper methods for extracting parameters.
@Component
@Aspect
public class RequestLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);
@Pointcut("execution(* your_package.controller..*(..))")
public void requestServer() {}
// ... (advices and helper methods as shown above)
}Developers are encouraged to integrate this aspect into their Spring applications to obtain comprehensive request logs, simplify debugging, and improve observability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
