SpringBoot基于AOP的Controller实现统一日志打印的完整方案

 更新时间:2026年04月03日 08:43:18   作者:谢正元  
本文基于Spring AOP实现一套无侵入、可配置、灵活扩展的Controller层统一日志打印方案,自动记录接口全量信息,同时支持慢接口监控告警,完全满足开发排查问题的核心需求

在日常后端开发中,接口问题排查效率低是最常见的痛点之一:很多项目没有统一的日志规范,Controller接口缺少请求URL、入参、响应结果、执行耗时等核心日志,导致线上报错、接口异常时无法快速定位问题;如果手动在每个接口中打印日志,又会造成代码冗余、维护成本高,还容易遗漏。

为了解决这个问题,本文基于Spring AOP实现一套无侵入、可配置、灵活扩展的Controller层统一日志打印方案,自动记录接口全量信息,同时支持慢接口监控告警,完全满足开发排查问题的核心需求。

一、方案核心优势

  • 无侵入性:无需修改任何Controller业务代码,通过切面统一处理日志逻辑
  • 全量信息:自动打印接口URL、请求方法、入参、响应结果、执行耗时
  • 安全可靠:过滤Web核心对象,避免序列化异常
  • 灵活配置:慢接口阈值可动态调整,支持按需开关/裁剪日志
  • 慢接口监控:自动识别超时接口,以WARN级别告警,助力性能优化
  • 统一规范:所有接口日志格式一致,便于日志检索和问题排查

二、环境依赖

项目基于Spring Boot开发,只需引入3个核心依赖(Maven):

<!-- Spring AOP 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- FastJSON 用于参数序列化 -->
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.32</version>
</dependency>
<!-- Lombok 简化日志开发 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

三、核心实现代码

直接创建AOP日志切面类,复制即用,适配绝大多数Spring Boot项目:

package com.xm.kite.ztc.common.aspect;

import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

/**
 * AOP统一日志处理切面
 * 自动打印Controller层接口:URL、方法名、入参、响应结果、执行耗时
 * 支持慢接口告警配置
 */
@Slf4j
@Aspect
@Component
public class LoggerHandler {

    /**
     * 慢接口阈值(单位:毫秒)
     * 从配置文件读取,默认2000ms(2秒)
     */
    @Value("${method-logger.elapsed-time:2000}")
    private Long loggerHandlerElapsedTimeMs;

    /**
     * 切点:匹配指定包下所有Controller的所有方法
     * 可根据项目包路径灵活修改!!!
     */
    @Pointcut("execution(* com.xm.kite.ztc..controller..*.*(..))")
    public void allControllerMethods() {
    }

    /**
     * 环绕通知:包裹目标接口,执行前后统一处理日志
     */
    @Around("allControllerMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 记录接口开始时间
        long startTime = System.currentTimeMillis();

        // 2. 获取Http请求对象,解析请求URL
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestUrl = request.getRequestURL().toString();

        // 3. 获取当前接口的类、方法名
        Logger currentLogger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
        String methodName = joinPoint.getSignature().getName();

        // 4. 安全处理入参:过滤HttpServletRequest/Response,避免序列化异常
        List<Object> safeArgs = new ArrayList<>();
        for (Object arg : joinPoint.getArgs()) {
            if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                safeArgs.add("[" + arg.getClass().getSimpleName() + "]");
            } else {
                safeArgs.add(arg);
            }
        }
        // 入参序列化为JSON
        String requestArgs = JSONObject.toJSONString(safeArgs);

        // 5. 打印【请求入参】日志
        currentLogger.info("请求开始 >>> url: {}, method: {}, args: {}", requestUrl, methodName, requestArgs);

        // 6. 执行目标接口方法
        Object result = joinPoint.proceed();

        // 7. 计算接口耗时
        long elapsedTime = System.currentTimeMillis() - startTime;
        String responseResult = JSONObject.toJSONString(result);

        // 8. 分级打印【响应结果】日志:慢接口WARN告警,正常接口INFO打印
        if (elapsedTime > loggerHandlerElapsedTimeMs) {
            currentLogger.warn("请求结束【慢接口告警】>>> 阈值:{}ms, url: {}, method: {}, args: {}, result: {}, 耗时:{}ms",
                    loggerHandlerElapsedTimeMs, requestUrl, methodName, requestArgs, responseResult, elapsedTime);
        } else {
            currentLogger.info("请求结束 >>> url: {}, method: {}, args: {}, result: {}, 耗时:{}ms",
                    requestUrl, methodName, requestArgs, responseResult, elapsedTime);
        }

        return result;
    }
}

四、灵活配置(核心!支持自定义调整)

1. 配置慢接口阈值

application.yml/application.properties动态调整慢接口标准,无需修改代码:

# 日志配置:慢接口阈值(单位:毫秒)
method-logger:
  elapsed-time: 1500  # 调整为1.5秒,根据项目需求修改

2. 灵活调整打印内容(按需改造)

针对不同开发场景,可快速裁剪/扩展日志打印内容,以下是常用扩展方案:

(1)添加请求IP、请求方式

// 获取请求方式
String requestMethod = request.getMethod();
// 获取客户端IP
String clientIp = request.getRemoteAddr();
// 日志中新增打印
currentLogger.info("url: {}, ip: {}, requestType: {}, method: {}, args: {}", 
        requestUrl, clientIp, requestMethod, methodName, requestArgs);

(2)过滤敏感参数(密码、手机号等)

// 改造入参过滤逻辑,隐藏敏感字段
for (Object arg : safeArgs) {
    if (arg instanceof UserDTO) {
        UserDTO user = (UserDTO) arg;
        user.setPassword("***"); // 密码脱敏
    }
}

(3)日志总开关(按需开启/关闭)

// 新增配置开关
@Value("${method-logger.enabled:true}")
private boolean loggerEnabled;

// 环绕通知中判断
@Around("allControllerMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    if (!loggerEnabled) {
        return joinPoint.proceed(); // 关闭日志,直接执行接口
    }
    // 原有日志逻辑...
}

(4)修改切点范围

// 示例1:只打印指定包下的接口
@Pointcut("execution(* com.xm.kite.ztc.user.controller..*.*(..))")
// 示例2:只打印带有@GetMapping注解的方法
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")

(5)关闭响应结果打印(简化日志)

如果接口返回数据过大,可取消响应结果打印:

// 直接删除result参数,简化日志
currentLogger.info("请求结束 >>> url: {}, 耗时:{}ms", requestUrl, elapsedTime);

五、代码核心解析

  • 切点定义@Pointcut精准匹配项目中所有Controller接口,是AOP日志的生效范围
  • 环绕通知@Around是核心,在接口执行前后分别记录请求、响应信息
  • 参数安全处理:过滤HttpServletRequest/Response对象,避免JSON序列化失败
  • 日志分级:正常接口用INFO级别,慢接口用WARN级别告警,便于日志筛选
  • 动态配置:通过@Value注入配置参数,支持环境差异化配置

六、日志效果展示

启动项目后,调用任意Controller接口,控制台会打印标准化日志:

# 正常接口
INFO  c.x.k.z.t.controller.UserController - 请求开始 >>> url: http://localhost:8080/user/get, method: getUser, args: {"id":1}
INFO  c.x.k.z.t.controller.UserController - 请求结束 >>> url: http://localhost:8080/user/get, method: getUser, args: {"id":1}, result: {"code":200,"data":{"name":"张三"}}, 耗时:12ms

# 慢接口告警
WARN  c.x.k.z.t.controller.OrderController - 请求结束【慢接口告警】>>> 阈值:2000ms, url: http://localhost:8080/order/list, method: getOrderList, args: {"page":1}, result: {...}, 耗时:2560ms

示例:

七、注意事项

  • AOP生效:Spring Boot 2.x+引入spring-boot-starter-aop后,无需额外配置 @EnableAspectJAutoProxy,自动开启AOP
  • 序列化问题:如果实体类存在循环引用,FastJSON会自动忽略,无需担心报错
  • 性能影响:日志打印为轻量级操作,对接口性能无明显损耗
  • 包路径修改:必须将切点中的包路径com.xm.kite.ztc改为自己项目的实际包路径,否则日志不生效

总结

这套基于Spring AOP的统一日志方案,彻底解决了接口排查无日志、手动打印日志冗余的问题,同时具备极高的灵活性:开发人员可根据需求自由调整打印内容、开关日志、配置慢接口阈值。

方案无侵入、易扩展、标准化,完全适配企业级Spring Boot项目,大幅提升线上问题排查效率和接口性能监控能力。

到此这篇关于SpringBoot基于AOP的Controller实现统一日志打印的完整方案的文章就介绍到这了,更多相关SpringBoot AOP打印日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈TreeSet中的两种排序方式

    浅谈TreeSet中的两种排序方式

    下面小编就为大家带来一篇浅谈TreeSet中的两种排序方式。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • Java实现数独小游戏

    Java实现数独小游戏

    这篇文章主要为大家详细介绍了Java实现数独小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-05-05
  • Java方法的覆盖与隐藏的区别分析

    Java方法的覆盖与隐藏的区别分析

    本篇文章介绍了,关于Java方法的覆盖与隐藏的区别分析。需要的朋友参考下
    2013-04-04
  • Spring Boot @Conditional注解用法示例介绍

    Spring Boot @Conditional注解用法示例介绍

    这篇文章主要给大家介绍了关于Spring Boot @Conditional注解用法的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-11-11
  • 设计模式之模版方法模式_动力节点Java学院整理

    设计模式之模版方法模式_动力节点Java学院整理

    这篇文章主要介绍了设计模式之模版方法模式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • java排序高级之选择排序实现方法

    java排序高级之选择排序实现方法

    这篇文章主要介绍了java排序高级之选择排序实现方法,较为全面的分析了选择排序的原理与具体实现技巧,非常具有实用价值,需要的朋友可以参考下
    2015-02-02
  • Java设计模式之中介模式

    Java设计模式之中介模式

    这篇文章主要介绍了Java设计模式之中介模式,中介模式(Mediator Pattern),属于行为型设计模式,目的是把系统中对象之间的调用关系从一对多转变成一对一的调用关系,以此来降低多个对象和类之间的通信复杂性,需要的朋友可以参考下
    2023-12-12
  • 浅谈Spring自定义注解从入门到精通

    浅谈Spring自定义注解从入门到精通

    这篇文章主要介绍了浅谈Spring自定义注解从入门到精通,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • SpringBoot如何使用Scala进行开发的实现

    SpringBoot如何使用Scala进行开发的实现

    这篇文章主要介绍了SpringBoot如何使用Scala进行开发的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • java实现图形卡片排序游戏

    java实现图形卡片排序游戏

    这篇文章主要为大家详细介绍了java实现图形卡片排序游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07

最新评论