Spring的AOP进行每个接口操作记录日志

该代码实现了一个基于SpringAOP的Web日志切面,用于记录控制器层的方法调用、请求参数、返回内容以及异常信息。自定义注解`OperationLog`用于标记需要记录日志的方法,使用`@Before`、`@AfterReturning`、`@AfterThrowing`和`@After`来处理方法执行的不同阶段。同时,定义了枚举类`OperationType`来表示不同的操作类型,如新增、删除等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、aop类

package com.skyable.common.aop;

import com.skyable.common.annotation.OperationLog;
import com.skyable.common.anscy.AsyncService;
import com.skyable.common.config.CloudApplicationContext;
import com.skyable.common.constants.param.CommonParams;
import com.skyable.common.dto.CommonDTO;
import com.skyable.common.dto.LogDTO;
import com.skyable.common.utils.IpAddressUtil;
import com.skyable.common.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
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;
import java.util.Objects;

/**
 * 类功能描述。
 *
 * @Filename: WebLogAcpect.java
 * @Description:
 * @Version: 1.0
 * @Author: 老友
 * @History: Author: GaoYuan/Date: 2022年11月07日/Version:1.0/Content: create
 */
@Aspect
@Component
@Slf4j
public class WebLogAspect {
    private final AsyncService asyncService;

    public WebLogAspect(AsyncService asyncService) {
        this.asyncService = asyncService;
    }


    /**
     * 定义切入点,切入点为com.skyable.device.controller下的所有函数
     */
    @Pointcut("@annotation(com.skyable.common.annotation.OperationLog)")
    public void webLog() {
    }

    /**
     * 前置通知:在连接点之前执行的通知
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String methodName = joinPoint.getSignature().getName();
        //设置ThreadLocal值
        String domainId = request.getHeader(CommonParams.HEADER_USER_DOMAIN_ID);
        if (!StringUtil.isEmpty(domainId)) {
            CloudApplicationContext.domainId.set(Long.valueOf(domainId));
        }
        String userName = request.getHeader(CommonParams.HEADER_USER_NAME);
        if (!StringUtil.isEmpty(userName)) {
            CloudApplicationContext.user.set(userName);
        }
        CloudApplicationContext.joinPoint.set(joinPoint);
        // 记录下请求内容
        log.info("method[" + methodName + "]request path: " + request.getRequestURL().toString());
        log.info("method[" + methodName + "]request method: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("method[" + methodName + "]request param: " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 后置通知
     *
     * @param joinPoint
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        OperationLog operationLog = signature.getMethod().getAnnotation(OperationLog.class);
        saveLog(operationLog, 0, CommonDTO.INFO);
        String methodName = joinPoint.getSignature().getName();
        // 处理完请求,返回内容
        log.info("method[" + methodName + "]return content: " + ret);
    }

    // /**
    //  * 环绕通知:
    //  *   环绕通知非常强大,可以决定目标method是否执行,什么时候执行,执行时是否需要替换method参数,执行完毕是否需要替换返回值。
    //  *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
    //  */
    // @Around(value = "webLog()")
    // public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    //     String methodName = proceedingJoinPoint.getSignature().getName();
    //     long startTime = System.currentTimeMillis();
    //     Object obj = proceedingJoinPoint.proceed();
    //     long endTime = System.currentTimeMillis();
    //     log.info("method["+methodName+"]deal tine: " + (endTime - startTime) + " ms");
    //     return obj;
    // }

    /**
     * 该注解标注的method在所有的Advice执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用;
     *
     * @param joinPoint
     */
    @After(value = "webLog()")
    public void logEnd(JoinPoint joinPoint) {
        //移除ThreadLocal值
        removeThreadLocal();
        String methodName = joinPoint.getSignature().getName();
        log.info("method[" + methodName + "]remove thread local content");
    }

    private void removeThreadLocal() {
        CloudApplicationContext.domainId.remove();
        CloudApplicationContext.user.remove();
        CloudApplicationContext.joinPoint.remove();
    }

    /**
     * 异常通知
     *
     * @param ex
     */
    @AfterThrowing(throwing = "ex", value = "webLog()")
    public void logEnd(Exception ex) {
        JoinPoint joinPoint = CloudApplicationContext.joinPoint.get();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        OperationLog operationLog = signature.getMethod().getAnnotation(OperationLog.class);
        // 保存数据
        saveLog(operationLog, 1, ex.getMessage());
    }

    /**
     * 添加日志
     *
     * @param operationLog
     * @param status
     * @param failCause
     */
    private void saveLog(OperationLog operationLog, Integer status, String failCause) {
        try {
            String username = null;
            if (Objects.nonNull(CloudApplicationContext.user)) {
                username = CloudApplicationContext.user.get();
            }
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String ipAddr = IpAddressUtil.getIpAddr(attributes.getRequest());
            String requestURI = attributes.getRequest().getRequestURI();
            log.info("日志url:{}", requestURI);
            String module = attributes.getRequest().getRequestURI().split("/")[1];
            LogDTO logDTO = new LogDTO();
            logDTO.setFailCause(failCause)
                    .setIpAddr(ipAddr)
                    .setStatus(status)
                    .setLogDetails(operationLog.logDetails())
                    .setModule(module).setUsername(username)
                    .setOperate(operationLog.operate().getKey());
            asyncService.saveLog(logDTO);
        } catch (Exception e) {
            removeThreadLocal();
        }

    }

}

二、自定义注解

package com.skyable.common.annotation;


import com.skyable.common.enums.OperationType;

import java.lang.annotation.*;

/**
 * @author  老友
 * @date 2022/12/10 15:19
 * @desc 操作日志注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperationLog {

    OperationType operate(); //操作类型
    String logDetails(); //日志详情

}

三、操作日志枚举类

package com.skyable.common.enums;

/**
 * @author   老友
 * @date 2022/12/10 15:19
 * @desc 操作日志枚举类
 */
public enum OperationType {
   /**
    * 操作类型
    */
   UNKNOWN("unknown","未知操作"),
   INSERT("insert","新增"),
   DELETE("delete","删除"),
   SELECT("select","查询"),
   UPDATE("update","更新"),
   LOGIN("login","用户登录-%s账户登录"),
   LOGOUT("logout","登出"),
   IMPORT("import","导入"),
   EXPORT("export","导出"),
   AUTH("auth","授权");

   private String key;
   private String value;

   public String getKey() {
      return key;
   }

   public void setKey(String key) {
      this.key = key;
   }

   public String getValue() {
      return value;
   }

   public void setValue(String value) {
      this.value = value;
   }

   OperationType(String key, String value) {
      this.key = key;
      this.value = value;
   }
}

四、日志结果类型

package com.skyable.common.enums;

/**
 * @author  老友
 * @title: LogType
 * @projectName private-cloud
 * @description: TODO
 * @date 2022/11/211:06
 */
public enum LogType {

    /**
     * INFO
     */
    INFO("info"),
    /**
     * WARNING
     */
    WARNING("warning"),
    /**
     * DEBUG
     */
    DEBUG("debug"),
    /**
     * ERROR
     */
    ERROR("error"),;

    private String logLevel;

    public String getLogLevel() {
        return logLevel;
    }

    public void setLogLevel(String logLevel) {
        this.logLevel = logLevel;
    }

    LogType(String logLevel) {
        this.logLevel = logLevel;
    }
}

五、日志controller

package com.skyable.account.controller;

import com.skyable.account.entity.Log;
import com.skyable.account.service.ILogService;
import com.skyable.common.common.ResponseResult;
import com.skyable.common.dto.CommonDTO;
import com.skyable.common.dto.LogDTO;
import com.skyable.common.dto.LogPageDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author :  老友
 * @Date: 2022/011/14 10:56
 * @Description: <描述>
 */
@RestController
@RequestMapping("/log")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Tag(name = "日志管理")
public class LogController {

    private final ILogService logService;

    @PostMapping("/saveLog")
    @Schema(name = "添加日志")
    public ResponseResult saveLog(@RequestBody LogDTO logDTO) {
        return logService.saveLog(logDTO);
    }

    @PostMapping("/logPage")
    @Schema(name = "查询日志分页")
    public ResponseResult logPage(@RequestBody @Validated LogPageDTO logPageDTO) {
        return logService.logPage(logPageDTO);

    }
}

六、controller注解

/**
     * 新增用户信息
     *
     * @param sysUser
     * @return
     */
    @Schema(description = "新增用户")
    @PostMapping("/insert")
    @OperationLog(operate = OperationType.INSERT, logDetails = "用户管理-新增用户")
    public ResponseResult insertRole(@RequestBody @Validated SysUser sysUser, HttpServletRequest request) {
        setInsertInfo(sysUser, request);
        return userService.insertUser(sysUser);

七、LogDTO

@Data
@Accessors(chain = true)
public class LogDTO implements Serializable {
    @Schema(name = "日志详情")
    private String logDetails;
    @Schema(name = "操作")
    private String operate;
    @Schema(name = "用户名")
    private String username;
    @Schema(name = "IP地址")
    private String ipAddr;
    @Schema(name = "状态 0 成功 1失败")
    private Integer status;
    @Schema(name = "失败原因")
    private String failCause;
    @Schema(name = "所属模块")
    private String module;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值