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

一、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;
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
具体实现方法如下: 1. 定义一个拦截器类,实现org.aopalliance.intercept.MethodInterceptor接口。 2. 在拦截器类中定义一个切入点,用于确定需要记录的方法。 3. 在切入点上定义一个通知,用于记录方法的每条记录。 4. 在通知中,调用日志框架的接口记录方法的每条记录。 以下是一个示例代码: ```java import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.log4j.Logger; public class LoggingInterceptor implements MethodInterceptor { private static final Logger logger = Logger.getLogger(LoggingInterceptor.class); // 定义切入点 private static final String POINTCUT = "execution(* com.example.service.*.*(..))"; @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 判断方法是否在切入点上 if (invocation.getMethod().getDeclaringClass().getName().matches(POINTCUT)) { // 获取方法参数 Object[] args = invocation.getArguments(); // 记录每条记录 for (Object arg : args) { logger.info("Method: " + invocation.getMethod().getName() + ", Arg: " + arg); } } // 执行方法 Object result = invocation.proceed(); return result; } } ``` 在上述示例代码中,LoggingInterceptor是拦截器类,实现了MethodInterceptor接口。在invoke()方法中,判断方法是否在切入点上,如果是,则记录方法的每条记录。在通知中,调用了Logger类的info()方法来记录日志。POINTCUT变量定义了切入点,用于匹配需要记录日志的方法。在这个示例中,POINTCUT匹配com.example.service包下的所有方法。 最后,在Spring配置文件中配置AOP,将LoggingInterceptor作为拦截器,并将其应用于需要记录日志的方法上。以下是一个示例配置: ```xml <bean id="loggingInterceptor" class="com.example.LoggingInterceptor"/> <aop:config> <aop:aspect ref="loggingInterceptor"> <aop:pointcut id="loggingPointcut" expression="execution(* com.example.service.*.*(..))"/> <aop:around method="invoke" pointcut-ref="loggingPointcut"/> </aop:aspect> </aop:config> ``` 在上述配置中,loggingInterceptor是LoggingInterceptor类的实例,用于记录日志。pointcut表达式和LoggingInterceptor类中的POINTCUT变量相同,用于确定需要记录日志的方法。在aop:around标签中,将LoggingInterceptor类的invoke()方法作为通知方法,与loggingPointcut切入点关联起来,用于记录日志

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值