自定义注解实现日志切面

注解

package com.ddb.common.annotation;

import org.omg.CORBA.DynAnyPackage.Invalid;

import javax.xml.crypto.Data;
import java.lang.annotation.*;
import java.time.LocalDateTime;

import static java.time.LocalDateTime.*;

/**
 * 日志记录注解
 *
 * @author zqf
 * @date 7/16/2019
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

    /**
     * 操作名称
     */
    String value();

    /**
     * 是否记录失败操作, 默认不记录
     */
    boolean fail() default false;

    /**
     * 是否记录入参, 默认记录
     */
    boolean args() default true;

    /**
     * 操作者: 默认取登录信息, 如果未登录, 需要从入参参数中获取或者自定义设置, 支持SPEL表达式取值
     */
    String operator() default "";

    /**
     * 操作者名称: 默认通过登录信息获取, 需要从入参参数中获取或者自定义设置, 支持SPEL表达式取值
     */
    String name() default "";

    /**
     *登录的ip
     */
    String ip() default "";

    /**
     * 日志类型 1 是登录类型 2 是操作类型
     * @return
     */
    int type() default 1;

}

切面类

package com.ddb.common.log;

import com.alibaba.fastjson.JSON;
import com.ddb.common.annotation.Log;
import com.ddb.model.result.Result;
import com.ddb.common.utils.LoginUserUtils;
import com.ddb.model.entity.DdbLog;
import com.ddb.service.DdbLogService;
import org.apache.commons.lang3.StringUtils;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;

/**
 * @author Duhuafei
 * @date 7/16/2019
 */
@Order(2)
@Aspect
@Component
public class LogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    DdbLogService ddbLogService;
    /**
     * 定义切点 @Pointcut
     */
    @Pointcut("@annotation( com.ddb.common.annotation.Log)")
    public void logPoinCut() {
    }

    /**
     * 切面 配置通知
     */
    @Around("logPoinCut()")
    public Object saveSysLog(ProceedingJoinPoint joinPoint) throws Throwable {
        LOGGER.debug("=========开始记录日志==========");

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //获取切入点所在的方法
        Method method = signature.getMethod();

        //获取操作
        Log log = method.getAnnotation(Log.class);

        //保存日志
        String value = log.value();
        LOGGER.debug("操作:{}", value);

        //保存获取的操作
        DdbLog logAdd = new DdbLog();
        logAdd.setOperation(value);
        logAdd.setType(log.type());
        Object[] args = joinPoint.getArgs();

        // 登录时,这里是获取不到用户信息的。
        LoginUserUtils.getUser()
                .ifPresent(user -> {
                    LOGGER.debug("用户信息:{}", user);
                    logAdd.setOperatorName(user.getFullName());
                    logAdd.setUsername(user.getUsername());
                    logAdd.setIp(user.getIp());
                });

        // 处理未登录时通过spel表达式获取参数值来设置 operator, fullName
        if (StringUtils.isNotBlank(log.operator())) {
            logAdd.setUsername(this.getSpelValue(log.operator(), method, args));
        }

        if (StringUtils.isNotBlank(log.name())) {
            logAdd.setOperatorName(this.getSpelValue(log.name(), method, args));
        }

        // 记录操作时间
        logAdd.setOperateTime(LocalDateTime.now());
        logAdd.setCreateTime(LocalDateTime.now());

        final Object proceed;
        try {
            // 调用执行方法
            proceed = joinPoint.proceed();
            LOGGER.debug("返回值:{}", JSON.toJSONString(proceed));

            // 异步保存日志 这个地方是自己的日志业务代码
            CompletableFuture.runAsync(()->ddbLogService.insert(logAdd));

            return proceed;
        } catch (Throwable throwable) {
            throw throwable;
        } finally {
            LOGGER.debug("=========结束记录日志==========");
        }
    }

    private String getSpelValue(String spel, Method method, Object[] args) {
        try {
            LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] parameterNames = discoverer.getParameterNames(method);

            if (null != parameterNames) {
                //使用SPEL进行key的解析
                ExpressionParser parser = new SpelExpressionParser();
                //SPEL上下文
                StandardEvaluationContext context = new StandardEvaluationContext();
                //把方法参数放入SPEL上下文中
                for (int i = 0; i < parameterNames.length; i++) {
                    context.setVariable(parameterNames[i], args[i]);
                }
                return parser.parseExpression(spel).getValue(context, String.class);
            }
        } catch (Exception e) {
            LOGGER.error("SPEL取值错误", e);
        }
        return spel;
    }

    private void buildOperatorStatus(LogAdd logAdd, Object proceed) {
        try {
            Result result = (Result) proceed;
            if (HttpStatus.OK.value() == result.getCode()) {
                logAdd.setOperatorStatus("成功");
                logAdd.setRemark("");
            } else {
                logAdd.setOperatorStatus("失败");
                logAdd.setRemark(result.getMessage());
            }
        } catch (Exception e) {
            logAdd.setRemark(JSON.toJSONString(proceed));
        }
    }
}

实体类

package com.ddb.common.log;

;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.math.BigInteger;
import java.time.LocalDateTime;

/**
 * @author zqf
 * @date 2/15/2019
 */

@Data
@Api("新增系统日志")
public class LogAdd {

    @ApiModelProperty("系统")
    private String from;

    @ApiModelProperty("ip")
    private String ip;

    @ApiModelProperty("使用人员")
    private String operator;

    @ApiModelProperty("操作者名称")
    private String fullName;

    @ApiModelProperty("操作名称")
    private String operation;

    @ApiModelProperty("操作状态")
    private String operatorStatus;

    @ApiModelProperty("备注")
    private String remark;

    @ApiModelProperty("操作对象")
    private String object;

    @ApiModelProperty("操作时间")
    private LocalDateTime operateTime;
}



package com.ddb.model.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.time.LocalDateTime;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author zqf
 * @Date 2022/8/8 15:07
 * @Description: $description
 */

/**
 * 日志表
 */
@ApiModel(value = "日志表")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DdbLog {
    /**
     * 主键id
     */
    @ApiModelProperty(value = "主键id")
    private Integer id;

    /**
     * ip
     */
    @ApiModelProperty(value = "ip")
    private String ip;

    /**
     * 操作者名称
     */
    @ApiModelProperty(value = "操作者名称")
    private String operatorName;

    /**
     * 操作者账号
     */
    @ApiModelProperty(value = "操作者账号")
    private String username;

    /**
     * 操作名称
     */
    @ApiModelProperty(value = "操作名称")
    private String operation;

    /**
     * 1是登录日志 2是操作日志
     */
    @ApiModelProperty(value = "1是登录日志 2是操作日志")
    private Integer type;


    /**
     * 操作时间
     */
    @ApiModelProperty(value = "操作时间")
    private LocalDateTime operateTime;

    /**
     * 创建时间
     */
    @ApiModelProperty(value = "创建时间")
    private LocalDateTime createTime;
}

使用方法

 /**
     * 用户登录成功
     * @param param 参数
     */
    @Log(value = "登录系统", fail = true, operator = "#param.username", name = "#param.fullName", ip = "#param.ip",type = 1)
    public Result loginSuccess(LoginUser param) {
        return Result.build().ok();
    }


    /**
     * 用户不存在时记录登录失败日志
     * @param param 参数
     */
    @Log(value = "登录失败,不存在该用户", fail = true, operator = "#param.username", args = false,type = 1)
    public Result loginFailNoUser( SignInDTO param, String msg) {
        return Result.build().error().withMessage(msg);
    }


    /**
     * 用户存在时记录登录失败日志
     * @param param 参数
     */
    @Log(value = "登录失败", fail = true, operator = "#param.username", args = false,type = 1)
    public Result loginFail(DdbUser param, String msg) {
        return Result.build().error().withMessage(msg);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值