切面编程实现系统的操作日志

一、首先系统引用以下依赖

<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>net.minidev</groupId>
            <artifactId>json-smart</artifactId>
            <version>2.3</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
            <classifier>jdk15</classifier>
        </dependency>

二、准备好表

CREATE TABLE `request_log` (
  `id` int(9) NOT NULL AUTO_INCREMENT,
  `status` varchar(2) DEFAULT NULL COMMENT '请求状态{0失败;1成功}',
  `request` text CHARACTER SET utf8 COLLATE utf8_bin COMMENT '请求参数',
  `response` text CHARACTER SET utf8 COLLATE utf8_bin COMMENT '返回参数',
  `error` text CHARACTER SET utf8 COLLATE utf8_bin COMMENT '错误信息',
  `type` varchar(20) DEFAULT NULL COMMENT '操作说明{30001增加;30002删除;30003修改}',
  `method` varchar(20) DEFAULT NULL COMMENT '请求方式',
  `created_by` varchar(32) DEFAULT NULL COMMENT '创建者',
  `created_at` bigint(20) DEFAULT NULL COMMENT '创建时间',
  `updated_by` varchar(32) DEFAULT NULL COMMENT '修改人',
  `updated_at` bigint(20) DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 COMMENT='请求日志表';

三、开始正文

生成好实体类
/**
 * <p>
 * 请求日志表
 * </p>
 *
 * @author LAZ
 * @since 2019-10-09
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("request_log")
public class RequestLog implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer id;

    /**
     * 请求结果标识
     */
    private String status;

    /**
     * 请求参数
     */
    private String request;

    /**
     * 返回参数
     */
    private String response;

    /**
     * 错误信息
     */
    private String error;

    /**
     * 操作说明{30001增加;30002删除;30003修改}
     */
    private Integer type;

    /**
     * 请求方式
     */
    private String method;

    /**
     * 创建人
     */
    private String createdBy;

    /**
     * 创建时间
     */
    private Long createdAt;

    /**
     * 更新人
     */
    private String updatedBy;

    /**
     * 更新时间
     */
    private Long updatedAt;


}
定义好一个枚举
/**
 * @author laz
 * @version 1.0
 * @date 2019/10/9 10:31
 */
public enum LogType {
    /**
     * 应用-增加
     */
    APP_ADD(30001),
    /**
     * 应用-删除
     */
    APP_DELETE(30002),
    /**
     * 应用-修改
     */
    APP_UPDATE(30003),
    /**
     * 部署-部署应用
     */
    DEP_ADD(40001),
    /**
     * 部署-回滚应用
     */
    DEP_ROLLBACK(40002);

    private int value;

    LogType(int value) {
        this.value = value;
    }

    LogType(String value) {
        for (LogType item : values()) {
            if (item.name().equals(value)) {
                this.value = item.value;
            }
        }
        throw new IllegalArgumentException("Invalid type value");
    }

    public int value() {
        return value;
    }

    public static LogType valueOf(int value) {
        for (LogType item : values()) {
            if (item.value() == value) {
                return item;
            }
        }
        throw new IllegalArgumentException("Invalid type value");
    }
}

自定义一个注解
import java.lang.annotation.*;

/**
 * 使用方式 @LogInfo(logType = LogType.APP_ADD)
 * @author laz
 * @version 1.0
 * @date 2019/10/9 10:31
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogInfo {

    /**
     * 描述
     * @return
     */
    public String description() default "";

    /**
     * 日志类型
     * @return
     */
    public LogType logType();
}

编写切面代码
package com.hnmqet.transdata.config;

import cn.hutool.core.util.ReflectUtil;
import com.hnmqet.framework.model.BasePageResult;
import com.hnmqet.transdata.common.Constant;
import com.hnmqet.transdata.entity.RequestLog;
import com.hnmqet.transdata.service.RequestLogService;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author laz
 * @version 1.0
 * @date 2019/10/9 10:31
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class LogAop {

    @Autowired
    private RequestLogService requestLogService;

    private ThreadLocal<RequestLog> threadLocal = new ThreadLocal<>();

	//这个地方就是切点
	//这个路径是你的自定义注解路径
    @Pointcut("@annotation(com.hnmqet.transdata.config.LogInfo)")
    public void controllerMethodPointcut() {
    }

    /**
     * 前置advice
     * @param point
     */
    @Before("controllerMethodPointcut()")
    public void before(JoinPoint point) {
        RequestLog logEntity = new RequestLog();
        //将当前实体保存到threadLocal
        threadLocal.set(logEntity);
        //获取连接点的方法签名对象,在该对象中可以获取到目标方法名,所属类的Class等信息
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取到该方法@LogInfo注解中的日志类型:枚举类LogType的值,保存到log实体中
        logEntity.setType(signature.getMethod().getAnnotation(LogInfo.class).logType().value());

        //RequestContextHolder:持有上下文的Request容器,获取到当前请求的request
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest httpServletRequest = sra.getRequest();

        logEntity.setMethod(httpServletRequest.getMethod());

        JSONObject jsonObject = new JSONObject();
        //存储uri到json中
        jsonObject.accumulate("uri", httpServletRequest.getRequestURI().toString());

        //这一步获取到的方法有可能是代理方法也有可能是真实方法
        Method m = ((MethodSignature) point.getSignature()).getMethod();
        //判断代理对象本身是否是连接点所在的目标对象,不是的话就要通过反射重新获取真实方法
        if (point.getThis().getClass() != point.getTarget().getClass()) {
            m = ReflectUtil.getMethod(point.getTarget().getClass(), m.getName(), m.getParameterTypes());
        }
        //通过真实方法获取该方法的参数名称
        LocalVariableTableParameterNameDiscoverer paramNames = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = paramNames.getParameterNames(m);

        //获取连接点方法运行时的入参列表
        Object[] args = point.getArgs();
        //将参数名称与入参值一一对应起来
        Map<String, Object> params = new HashMap<>();
        for (int i = 0; i < parameterNames.length; i++) {
            params.put(parameterNames[i], args[i]);
        }
        jsonObject.accumulate("params", params);
        //为log实体类的request字段赋值
        logEntity.setRequest(jsonObject.toString());
        System.out.println("============================ 》Before : " + logEntity.toString());
    }

    /**
     * 方法成功return之后的advice
     * @param point
     * @param rtv
     */
    @AfterReturning(value = "controllerMethodPointcut()", returning = "rtv")
    public void after(JoinPoint point, Object rtv) {
        //得到当前线程的log对象
        RequestLog log = threadLocal.get();
        //rtv为controller方法返回数据
        JSONObject jsonObject = JSONObject.fromObject(rtv);
        //为log实体的response字段赋值
        log.setResponse(jsonObject.toString());
        log.setStatus(Constant.REQUEST_SUCCESS);
        //插入一条log信息
        requestLogService.save(threadLocal.get());
        //移除当前log实体
        threadLocal.remove();
        System.out.println("============================ 》AfterReturning : " + log.toString());
    }

    /**
     * 报错之后的advice
     * @param throwing
     */
    @AfterThrowing(value = "controllerMethodPointcut()", throwing = "throwing")
    public void error(Throwable throwing) {
        RequestLog log = threadLocal.get();
        log.setStatus(Constant.REQUEST_ERRORS);
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            //将报错信息写入error字段
            throwing.printStackTrace(new PrintStream(byteArrayOutputStream));
            log.setError(byteArrayOutputStream.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        requestLogService.save(threadLocal.get());
        threadLocal.remove();
        System.out.println("============================ 》AfterThrowing : " + log.toString());
    }
}


应用

在你需要记录日志的方法上添加这个注解就好了
在这里插入图片描述

成果

在这里插入图片描述
好了另外还需要添加什么字段都可以到上面进行添加

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值