全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-18 6 分钟 ✍️ juanwangdev

Spring MVC 拦截器实现日志记录

拦截器是记录请求日志的理想位置,可统一记录所有Controller请求的详细信息。

基本日志拦截器

Java
@Slf4j
@Component
public class LogInterceptor implements HandlerInterceptor {

    private static final String START_TIME = "startTime";

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        request.setAttribute(START_TIME, System.currentTimeMillis());

        log.info("请求开始: {} {} from {}",
            request.getMethod(),
            request.getRequestURI(),
            request.getRemoteAddr());

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        Long startTime = (Long) request.getAttribute(START_TIME);
        long duration = System.currentTimeMillis() - startTime;

        log.info("请求完成: {} {} 状态:{} 耗时:{}ms",
            request.getMethod(),
            request.getRequestURI(),
            response.getStatus(),
            duration);

        request.removeAttribute(START_TIME);
    }
}

详细日志记录

Java
@Slf4j
@Component
public class DetailedLogInterceptor implements HandlerInterceptor {

    private static final String START_TIME = "startTime";
    private static final String REQUEST_ID = "requestId";

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        String requestId = UUID.randomUUID().toString().substring(0, 8);
        request.setAttribute(REQUEST_ID, requestId);
        request.setAttribute(START_TIME, System.currentTimeMillis());

        StringBuilder logBuilder = new StringBuilder();
        logBuilder.append("\n========== 请求信息 ==========\n");
        logBuilder.append("RequestId: ").append(requestId).append("\n");
        logBuilder.append("Method: ").append(request.getMethod()).append("\n");
        logBuilder.append("URI: ").append(request.getRequestURI()).append("\n");
        logBuilder.append("Query: ").append(request.getQueryString()).append("\n");
        logBuilder.append("IP: ").append(request.getRemoteAddr()).append("\n");
        logBuilder.append("User-Agent: ").append(request.getHeader("User-Agent")).append("\n");

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            logBuilder.append("Handler: ")
                .append(handlerMethod.getBeanType().getSimpleName())
                .append(".")
                .append(handlerMethod.getMethod().getName())
                .append("\n");
        }

        logBuilder.append("Headers:\n");
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            logBuilder.append("  ").append(name).append(": ")
                .append(request.getHeader(name)).append("\n");
        }

        log.info(logBuilder.toString());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        Long startTime = (Long) request.getAttribute(START_TIME);
        String requestId = (String) request.getAttribute(REQUEST_ID);
        long duration = System.currentTimeMillis() - startTime;

        StringBuilder logBuilder = new StringBuilder();
        logBuilder.append("\n========== 响应信息 ==========\n");
        logBuilder.append("RequestId: ").append(requestId).append("\n");
        logBuilder.append("Status: ").append(response.getStatus()).append("\n");
        logBuilder.append("Duration: ").append(duration).append("ms\n");

        if (ex != null) {
            logBuilder.append("Exception: ").append(ex.getClass().getName()).append("\n");
            logBuilder.append("Message: ").append(ex.getMessage()).append("\n");
        }

        log.info(logBuilder.toString());
    }
}

异常日志记录

Java
@Slf4j
@Component
public class ExceptionLogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        long duration = System.currentTimeMillis()
            - (Long) request.getAttribute("startTime");

        if (ex != null) {
            // 记录异常详情
            log.error("请求异常: {} {}", request.getMethod(), request.getRequestURI());
            log.error("异常类型: {}", ex.getClass().getName());
            log.error("异常信息: {}", ex.getMessage());
            log.error("处理耗时: {}ms", duration);
            log.error("堆栈信息: ", ex);
        } else {
            log.info("请求成功: {} {} 状态:{} 耗时:{}ms",
                request.getMethod(), request.getRequestURI(),
                response.getStatus(), duration);
        }
    }
}

日志级别分级

Java
@Slf4j
@Component
public class LogLevelInterceptor implements HandlerInterceptor {

    private static final long WARN_THRESHOLD = 1000;    // 1秒
    private static final long ERROR_THRESHOLD = 3000;   // 3秒

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        long duration = System.currentTimeMillis()
            - (Long) request.getAttribute("startTime");

        String method = request.getMethod();
        String uri = request.getRequestURI();
        int status = response.getStatus();

        if (ex != null) {
            log.error("[ERROR] {} {} - 异常: {}, 耗时: {}ms",
                method, uri, ex.getMessage(), duration);
        } else if (duration >= ERROR_THRESHOLD) {
            log.error("[SLOW] {} {} - 耗时: {}ms, 状态: {}",
                method, uri, duration, status);
        } else if (duration >= WARN_THRESHOLD) {
            log.warn("[WARN] {} {} - 耗时: {}ms, 状态: {}",
                method, uri, duration, status);
        } else {
            log.info("[INFO] {} {} - 耗时: {}ms, 状态: {}",
                method, uri, duration, status);
        }
    }
}

敏感信息过滤

Java
@Slf4j
@Component
public class SecureLogInterceptor implements HandlerInterceptor {

    private static final Set<String> SENSITIVE_HEADERS = Set.of(
        "Authorization", "Cookie", "X-Auth-Token", "Password"
    );

    private static final Set<String> SENSITIVE_PARAMS = Set.of(
        "password", "token", "secret", "creditCard"
    );

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        request.setAttribute("startTime", System.currentTimeMillis());

        String uri = filterSensitiveParams(request.getRequestURI(), request);
        String queryString = filterQueryString(request.getQueryString());

        log.info("请求开始: {} {}?{}", request.getMethod(), uri, queryString);
        return true;
    }

    private String filterSensitiveParams(String uri, HttpServletRequest request) {
        String result = uri;
        for (String param : SENSITIVE_PARAMS) {
            String value = request.getParameter(param);
            if (value != null) {
                result = result.replace(value, "***");
            }
        }
        return result;
    }

    private String filterQueryString(String queryString) {
        if (queryString == null) {
            return "";
        }

        StringBuilder filtered = new StringBuilder();
        String[] params = queryString.split("&");
        for (String param : params) {
            String[] kv = param.split("=");
            if (kv.length == 2 && SENSITIVE_PARAMS.contains(kv[0])) {
                filtered.append(kv[0]).append("=***&");
            } else {
                filtered.append(param).append("&");
            }
        }
        return filtered.length() > 0
            ? filtered.substring(0, filtered.length() - 1)
            : "";
    }
}

日志入库存储

Java
@Slf4j
@Component
public class DatabaseLogInterceptor implements HandlerInterceptor {

    @Autowired
    private RequestLogService requestLogService;

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        RequestLog logEntity = new RequestLog();
        logEntity.setRequestId(UUID.randomUUID().toString());
        logEntity.setMethod(request.getMethod());
        logEntity.setUri(request.getRequestURI());
        logEntity.setIp(request.getRemoteAddr());
        logEntity.setStartTime(LocalDateTime.now());

        request.setAttribute("logEntity", logEntity);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        RequestLog logEntity = (RequestLog) request.getAttribute("logEntity");
        if (logEntity == null) {
            return;
        }

        logEntity.setEndTime(LocalDateTime.now());
        logEntity.setStatus(response.getStatus());
        logEntity.setDuration(
            System.currentTimeMillis() - logEntity.getStartTime().toEpochSecond(ZoneOffset.UTC) * 1000
        );

        if (ex != null) {
            logEntity.setException(ex.getClass().getName());
            logEntity.setExceptionMessage(ex.getMessage());
        }

        // 异步保存到数据库
        requestLogService.saveAsync(logEntity);
    }
}

拦截器配置

Java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LogInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor)
            .addPathPatterns("/**")
            .excludePathPatterns(
                "/static/**",
                "/favicon.ico",
                "/error"
            )
            .order(Integer.MIN_VALUE);    // 最高优先级
    }
}

日志拦截器order值设为最小,确保最先执行记录完整耗时。

要点总结

  • preHandle记录请求开始时间和基本信息
  • afterCompletion计算耗时并记录完成日志
  • 根据耗时分级设置不同日志级别
  • 过滤敏感信息避免泄露密码、Token等
  • 生产环境可将日志异步存储到数据库
  • 日志拦截器order值应最小以记录完整耗时

📝 发现内容有误?点击此处直接编辑

← 上一篇 Spring MVC 拦截器与过滤器的区别
下一篇 → Spring MVC 拦截器实现权限控制
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库