springboot处理请求日志(AOP)

参考springboot+log4j统一处理请求日志(AOP)
有关请求参数部分做了微调,以及使用了项目内的Slf4j记录日志。

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

/**
 * @author heyang
 */
@Aspect
@Component
@Slf4j
public class WebLogAspect {


    @Pointcut("execution(public * com.hws.manager..controller.*.*(..))")
    public void webLog() {

    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        log.info("---------------request----------------");
        log.info("URL : " + request.getRequestURL().toString());
        log.info("HTTP_METHOD : " + request.getMethod());
        Map<String,Object> map = new HashMap<>();
        try {
            Object[] params = joinPoint.getArgs();
            for (int i = 0; i < params.length; i++) {
                if (params[i] instanceof BindingResult
                        || params[i] instanceof HttpRequest
                        || params[i] instanceof HttpResponse){
                    continue;
                }
                map.put("param-" + i,params[i]);
            }
        }catch (Exception e){

        }
        log.info("PARAMS_JSON : " + map);
    }
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("---------------response----------------");
        // 处理完请求,返回内容
        log.info("RESPONSE : " + ret);
    }
}
<think>我们正在使用Spring Boot AOP实现接口请求日志记录。根据引用内容,我们可以总结出以下步骤: 1. 添加依赖:在`pom.xml`中添加Spring Boot AOP依赖。 2. 创建切面类:使用`@Aspect`注解标记的类,定义切面。 3. 定义切点:通过表达式指定哪些方法需要记录日志(例如,所有Controller中的方法)。 4. 编写通知:在方法执行前后(或异常时)记录日志,可以使用`@Before`、`@After`、`@Around`等注解。 5. 获取请求信息:在通知方法中,可以通过`JoinPoint`获取方法名、参数等信息,也可以注入`HttpServletRequest`来获取请求信息。 6. 记录日志:将获取的信息输出到日志文件或数据库。 下面我们详细说明每个步骤。 ### 1. 添加依赖 在`pom.xml`中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` ### 2. 创建切面类 创建一个类,用`@Aspect`和`@Component`注解标记,这样Spring就会将其识别为一个切面并管理。 ### 3. 定义切点 使用`@Pointcut`注解定义切点表达式,例如: ```java @Pointcut("execution(* com.example.demo.controller..*.*(..))") public void controllerPointcut() {} ``` 这个切点表示`com.example.demo.controller`包下所有类的所有方法。 ### 4. 编写通知 我们通常使用`@Around`通知,因为它可以在方法执行前后都进行操作,并且可以控制方法是否执行。但也可以使用`@Before`和`@After`。 ### 5. 获取请求信息 我们可以通过`JoinPoint`获取方法签名、参数等信息。同时,为了获取HTTP请求信息,我们可以注入`HttpServletRequest`。 ### 6. 记录日志 在通知方法中,我们可以使用日志框架(如SLF4J)记录日志,也可以将日志存入数据库(如引用[2]所述)。 下面是一个完整的切面类示例,记录每个Controller方法的请求信息(包括请求时间、方法名、参数等)和耗时: ```java 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.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; @Aspect @Component public class RequestLogAspect { private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class); // 定义切点:controller包下的所有方法 @Pointcut("execution(* com.example.demo.controller..*.*(..))") public void controllerPointcut() {} @Around("controllerPointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); // 获取请求信息 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录请求信息 logger.info("URL: {}", request.getRequestURL().toString()); logger.info("HTTP Method: {}", request.getMethod()); logger.info("IP: {}", request.getRemoteAddr()); logger.info("Class Method: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); logger.info("Args: {}", Arrays.toString(joinPoint.getArgs())); // 执行方法 Object result = joinPoint.proceed(); // 计算耗时 long endTime = System.currentTimeMillis(); logger.info("Response: {}", result); // 注意:如果返回结果很大,可能不适合打印,可以酌情处理 logger.info("Time Cost: {} ms", endTime - startTime); return result; } } ``` 注意:上面的示例中,我们打印了返回结果,这在生产环境中可能会因为返回结果过大而不适合,所以可以根据实际情况调整。 ### 扩展:记录到数据库 如果需要将日志记录到数据库,可以按照引用[2]中的方法设计数据库表,并在切面中调用Service层将日志信息保存到数据库。 ### 总结 通过以上步骤,我们可以在Spring Boot应用中利用AOP实现接口请求日志记录。这种方法非侵入式,只需添加一个切面类,即可统一处理所有Controller的请求日志。 §§相关问题§§ 1. 如何优化AOP日志记录的性能? 2. 如何避免记录敏感信息的日志? 3. 如何将日志记录到数据库?</think>### Spring Boot AOP 实现接口请求日志记录方法 以下是使用 Spring Boot AOP 实现接口请求日志记录的完整步骤: --- #### 1. **添加依赖** 在 `pom.xml` 中添加 AOP 依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` --- #### 2. **创建切面类** 定义切面类处理日志记录逻辑: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; @Aspect @Component public class RequestLogAspect { // 定义切点:拦截所有Controller层方法 @Pointcut("execution(* com.example.controller..*.*(..))") public void requestLogPointcut() {} // 前置通知:记录请求信息 @Before("requestLogPointcut()") public void logRequest(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 构建日志信息 String log = String.format("Request | Time: %s | IP: %s | Method: %s | URI: %s | Args: %s", LocalDateTime.now(), request.getRemoteAddr(), request.getMethod(), request.getRequestURI(), Arrays.toString(joinPoint.getArgs())); System.out.println(log); // 实际项目中替换为Logger } // 后置通知:记录响应信息 @AfterReturning(pointcut = "requestLogPointcut()", returning = "result") public void logResponse(JoinPoint joinPoint, Object result) { String log = String.format("Response | Time: %s | Method: %s | Result: %s", LocalDateTime.now(), joinPoint.getSignature().getName(), result); System.out.println(log); // 实际项目中替换为Logger } } ``` --- #### 3. **关键配置说明** - **切点表达式**:`execution(* com.example.controller..*.*(..))` 表示拦截 `com.example.controller` 包下所有类的所有方法。 - **获取请求信息**:通过 `RequestContextHolder` 获取当前 HTTP 请求对象。 - **日志内容**: - **请求日志**:时间、客户端 IP、HTTP 方法、URI、参数。 - **响应日志**:时间、方法名、返回值。 --- #### 4. **扩展功能** ##### (1) 记录到数据库(引用[2]) 设计日志表结构: ```sql CREATE TABLE api_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, ip VARCHAR(20), method VARCHAR(10), uri VARCHAR(255), args TEXT, result TEXT, create_time DATETIME ); ``` 在切面中注入 `JdbcTemplate` 或 `Repository` 保存日志。 ##### (2) 添加 Trace ID(引用[5]) 实现分布式链路追踪: ```java // 在请求头中添加 traceId String traceId = UUID.randomUUID().toString(); MDC.put("traceId", traceId); // 使用 SLF4J 的 MDC // 日志格式中增加 %X{traceId} // 示例:logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%X{traceId}] %-5level %logger{36} - %msg%n ``` ##### (3) 排除敏感参数 使用自定义注解过滤敏感字段: ```java @Before("requestLogPointcut()") public void logRequest(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { if (args[i] instanceof UserDTO) { args[i] = ((UserDTO) args[i]).maskPassword(); // 脱敏处理 } } // 记录脱敏后的参数 } ``` --- #### 5. **优化建议** 1. **性能优化**: - 异步记录日志:使用 `@Async` 注解异步保存日志。 - 批量插入:攒批处理数据库写入(如每 10 条批量插入)[^2]。 2. **动态开关**: 通过配置中心动态启停日志: ```java @Before("requestLogPointcut() && @annotation(enableLogging)") public void logIfEnabled(JoinPoint joinPoint, EnableLogging enableLogging) { if (enableLogging.value()) { ... } } ``` --- #### **总结** 通过 AOP 实现接口日志记录的核心步骤: 1. 添加 `spring-boot-starter-aop` 依赖。 2. 定义切面类,使用 `@Pointcut` 定位拦截范围。 3. 通过 `@Before` 记录请求信息,`@AfterReturning` 记录响应信息。 4. 结合 `HttpServletRequest` 和 `JoinPoint` 获取请求上下文。 5. 扩展功能:数据库存储、链路追踪、敏感信息过滤。 > 此方法避免了业务代码耦合,统一处理日志逻辑[^1][^3],适用于监控、审计、调试等场景[^5]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值