Mastering API Request Logging in Spring Boot 3: 7 Practical Techniques

This article presents seven practical methods for recording API request logs in Spring Boot 3, including Filter, HandlerInterceptor, AOP, event listeners, Logbook, custom HandlerMethod, and RestTemplate/RestClient interceptors, complete with code samples, configuration steps, and execution screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering API Request Logging in Spring Boot 3: 7 Practical Techniques

1. Introduction

Recording API request logs is essential for system security, maintainability, and performance monitoring. By capturing details such as request time, IP, method, parameters, response status, and latency, developers can trace errors, analyze user behavior, optimize performance, and ensure data safety.

2. Practical Cases

2.1 Filter

Using a Filter allows interception and logging of HTTP requests and responses before they reach the target resource.

@RestController
@RequestMapping("/api")
public class ApiController {
    private static final Logger logger = LoggerFactory.getLogger(ApiController.class);
    @GetMapping("/query/{category}")
    public ResponseEntity<Object> query(@PathVariable String category, @RequestParam String keyword) {
        logger.info("查询数据, 分类: {}, 关键词: {}", category, keyword);
        return ResponseEntity.ok("success");
    }
}

Register the filter:

@Bean
FilterRegistrationBean<Filter> loggingFilter() {
    FilterRegistrationBean<Filter> reg = new FilterRegistrationBean<>();
    CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
    filter.setIncludeClientInfo(true);
    filter.setIncludeHeaders(true);
    filter.setIncludePayload(true);
    filter.setIncludeQueryString(true);
    reg.setFilter(filter);
    reg.addUrlPatterns("/api/*");
    return reg;
}

Enable debug logging for the filter:

logging:
  level:
    '[org.springframework.web.filter]': debug

Result:

2.2 HandlerInterceptor

The HandlerInterceptor can log before and after controller execution, as well as on completion.

@Component
public class LoggingRequestInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        long startTime = Instant.now().toEpochMilli();
        String time = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        logger.info("Request URL::" + request.getRequestURL() + ":: StartTime=" + time);
        request.setAttribute("startTime", startTime);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        long startTime = (Long) request.getAttribute("startTime");
        logger.info("Request URL::" + request.getRequestURL() + ":: TimeTaken=" + (Instant.now().toEpochMilli() - startTime));
    }
}
@Component
public class InterceptorConfig implements WebMvcConfigurer {
    private final LoggingRequestInterceptor loggingRequestInterceptor;
    public InterceptorConfig(LoggingRequestInterceptor loggingRequestInterceptor) {
        this.loggingRequestInterceptor = loggingRequestInterceptor;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loggingRequestInterceptor).addPathPatterns("/api/**");
    }
}

Result screenshot:

2.3 AOP

Using AOP, logging can be added without modifying business code.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Log {
    String module() default "";
    String desc() default "";
}
@Component
@Aspect
public class LogAspect {
    @Pointcut("@annotation(log)")
    private void recordLog(Log log) {}
    @Around("recordLog(log)")
    public Object logAround(ProceedingJoinPoint pjp, Log log) throws Throwable {
        String module = log.module();
        String desc = log.desc();
        long uid = System.nanoTime();
        String threadName = Thread.currentThread().getName();
        System.err.printf("%s - 【%d】 模块: %s, 操作: %s, 请求参数: %s%n", threadName, uid, module, desc, Arrays.toString(pjp.getArgs()));
        Object ret = pjp.proceed();
        System.err.printf("%s - 【%d】 返回值: %s%n", threadName, uid, ret);
        return ret;
    }
}
@Log(module = "综合查询", desc = "查询商品信息")
@GetMapping("/query/{category}")
public ResponseEntity<Object> query(@PathVariable String category, @RequestParam String keyword) {
    return ResponseEntity.ok("success");
}

Result screenshot:

2.4 Servlet Request Event

Spring MVC publishes ServletRequestHandledEvent after a request is processed; listening to this event provides full request/response details.

@Component
public class RequestEventListener implements ApplicationListener<ServletRequestHandledEvent> {
    private static final Logger logger = LoggerFactory.getLogger(RequestEventListener.class);
    @Override
    public void onApplicationEvent(ServletRequestHandledEvent event) {
        logger.info("请求信息: {}", event);
    }
}

Result screenshot:

2.5 Logbook Component

Logbook is a third‑party library that records complete HTTP request and response logs.

<dependency>
  <groupId>org.zalando</groupId>
  <artifactId>logbook-spring-boot-starter</artifactId>
  <version>3.10.0</version>
</dependency>
logging:
  level:
    '[org.zalando.logbook.Logbook]': TRACE

Result screenshot:

2.6 Custom HandlerMethod

Custom HandlerMethod implementations can log efficiently for controller‑only scenarios. (Implementation details are linked in the original article.)

2.7 Third‑Party API Calls

When invoking external services with RestTemplate or RestClient, a ClientHttpRequestInterceptor can capture request and response data.

public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        logRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        logResponse(response);
        return response;
    }
    private void logRequest(HttpRequest request, byte[] body) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("===========================request begin================================================");
            log.debug("URI         : {}", request.getURI());
            log.debug("Method      : {}", request.getMethod());
            log.debug("Headers     : {}", request.getHeaders());
            log.debug("Request body: {}", new String(body, "UTF-8"));
            log.debug("==========================request end================================================");
        }
    }
    private void logResponse(ClientHttpResponse response) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("============================response begin==========================================");
            log.debug("Status code  : {}", response.getStatusCode());
            log.debug("Status text  : {}", response.getStatusText());
            log.debug("Headers      : {}", response.getHeaders());
            log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
            log.debug("=======================response end=================================================");
        }
    }
}
@Bean
RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder.additionalInterceptors(new LoggingClientHttpRequestInterceptor()).build();
}
@Bean
RestClient restClient() {
    return RestClient.builder()
        .requestInterceptor(new LoggingClientHttpRequestInterceptor())
        .build();
}

Result screenshot:

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopSpring BootfilterAPI logging
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.