Spring AOP 记录系统操作日志

问题:

  系统需要记录用户的关键操作日志,以便后期的系统维护,方便的查看问题,及时排除

 

分析:

  (1)保存字段:作为一个日志记录功能,首先数据库新建一张表保存用户的操作关键字段,

          用户名,ip,操作描述,时间,日志id

  (2)采用技术:

      第一种:新建一个日志业务实现,在操作发生时进行联动

          缺点是耦合太紧密,无用代码增多,后期代码臃肿,改动时地方分散,不利于维护

      第二种:使用spring 的 aop 技术进行切面切入

          由于本身系统结构不规范,参数,方法名没有一致的样式,使用正则匹配方法名不是很方便

          本身日志记录也只是记录关键操作,api全部切入的话,就不需要这个功能了

          必须有针对性的记录关键操作日志

      第三种:使用spring 的 aop 技术切到自定义注解上,针对不同注解标志进行参数解析,记录日志

          缺点是要针对每个不同的注解标志进行分别取注解标志,获取参数进行日志记录输出

         

解决:

  由于本身系统的不规范,目前采用第三种办法进行关键日志的记录

      我的日志记录表sql:

CREATE TABLE `ts_sys_operation_log` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
  `login_num` varchar(30) DEFAULT NULL COMMENT '登录工号',
  `login_name` varchar(32) DEFAULT NULL COMMENT '登录用户名',
  `method_name` varchar(500) NOT NULL COMMENT '方法名',
  `method_remark` varchar(100) DEFAULT NULL COMMENT '方法描述',
  `operation_content` varchar(1000) DEFAULT NULL COMMENT '操作内容',
  `ip_address` varchar(30) NOT NULL COMMENT 'IP地址',
  `create_tm` varchar(30) NOT NULL COMMENT '创建时间',
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统操作日志表';

  (1)首先新建 注解类【MethodLog】

package org.triber.portal.service.logAop;

import java.lang.annotation.*;

/**
 * 日志切面注解
 */

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {

    String remark() default "";
    String operType() default "0";
    // String desc() default "";
}

(2)新建切面实现类【LogService】


import com.alibaba.fastjson.JSON;
import com.github.pagehelper.StringUtil;
import com.google.common.collect.Maps;
import com.sf.ucs.nav.mapper.logaop.SysOperationLogMapper;
import com.sf.ucs.nav.mapper.prd.ProduceMapper;
import com.sf.ucs.nav.model.logaop.SysOperationLog;
import com.sf.ucs.sac.date.DateUtil;
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.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
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.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 日志切面实现
 */

@Component
@Aspect
public class LogService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private SysOperationLogMapper sysOperationLogMapper;

    @Autowired
    private ProduceMapper produceMapper;

    public LogService() {
    }

    /**
     * 切点
     */
    @Pointcut("@annotation(com.sf.ucs.nav.service.logaop.MethodLog)")
    public void methodCachePointcut() { }

    /**
     * 切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("methodCachePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Object object = point.proceed();
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();
            String ip = getIp(request);
            String methodRemark = getMthodRemark(point);
            String methodName = point.getSignature().getName();
            String packages = point.getThis().getClass().getName();
            if (packages.indexOf("$$EnhancerBySpringCGLIB$$") > -1) { // 如果是CGLIB动态生成的类
                packages = packages.substring(0, packages.indexOf("$$"));
            }

            Object[] method_param = point.getArgs(); //获取方法参数
            Map<String,Object> parammMap = null;
            if(method_param[0] instanceof Map){
                parammMap = (Map) method_param[0];
            }else if(method_param[0].getClass() instanceof Class){
                parammMap = beanToMap(method_param[0]);
            }else {
                parammMap = Maps.newHashMap();
            }
            String loginNum;
            String loginName = "";
            if(parammMap.get("creater") != null){
                loginNum = (String) parammMap.get("creater");
            }else if(parammMap.get("updater") != null){
                loginNum = (String) parammMap.get("updater");
            }else if(parammMap.get("createEmp") != null){
                loginNum = (String) parammMap.get("createEmp");
            }else if(parammMap.get("updaterEmp") != null){
                loginNum = (String) parammMap.get("updaterEmp");
            }else {
                loginNum = "匿名用户";
            }
            if(!"匿名用户".equals(loginNum)){
                Map<String, String> userMap = produceMapper.getUserInfo(loginNum);
                if(userMap != null){
                    loginName = userMap.get("userName");
                }
            }

            SysOperationLog sysLog = new SysOperationLog();
            sysLog.setLoginNum(loginNum);
            sysLog.setLoginName(loginName);
            sysLog.setMethodName(packages + "." + methodName);
            sysLog.setMethodRemark(methodRemark);
            sysLog.setIpAddress(ip);
            sysLog.setCreateTm(DateUtil.getNowStr());

            /**
             * 操作
             */
            if("".equals(methodRemark) || null == methodRemark){
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }else if(methodRemark.startsWith("新增")){
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }else if(methodRemark.startsWith("修改")){
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }else if(methodRemark.startsWith("删除")){
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }else if(methodRemark.startsWith("查询")){
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }else if(methodRemark.startsWith("导出")){
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }else {
                sysLog.setOperatingContent(methodRemark + ", 操作参数: " + JSON.toJSON(method_param[0]));
            }

            sysOperationLogMapper.insert(sysLog);
            logger.info("日志实体:"+sysLog.getLoginName()+sysLog.getMethodRemark()+sysLog.getOperatingContent());
        } catch (Exception e) {
            // 异常处理记录日志..log.error(e);
            logger.error("系统操作日志新增异常:{}", e.getMessage());
        }
        return object;
    }

    /**
     * 将对象装换为map
     *
     * @param bean
     * @return
     */
    public static <T> Map<String, Object> beanToMap(T bean) {
        Map<String, Object> map = Maps.newHashMap();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                map.put(key + "", beanMap.get(key));
            }
        }
        return map;
    }

    /**
     * 方法异常时调用
     *
     * @param ex
     */
    public void afterThrowing(Exception ex) {
        System.out.println("afterThrowing");
        System.out.println(ex);
    }

    /**
     * 获取方法中的中文备注
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception {

        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();

        Class targetClass = Class.forName(targetName);
        Method[] method = targetClass.getMethods();
        String methode = "";
        for (Method m : method) {
            if (m.getName().equals(methodName)) {
                Class[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length) {
                    MethodLog methodCache = m.getAnnotation(MethodLog.class);
                    if (methodCache != null) {
                        methode = methodCache.remark();
                    }
                    break;
                }
            }
        }
        return methode;
    }

    /**
     * 获取请求ip
     *
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip.substring(0, index);
            } else {
                return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr().equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }


}

(3)新建日志实体【SysOperationLog】

package com.sf.ucs.nav.model.logaop;

import java.io.Serializable;

/**
 * @Description:  操作日志实体类
 * @author jacky
 * @date 2019年8月27日 下午4:06:45
 */
public class SysOperationLog implements Serializable{
	private static final long serialVersionUID = 6791612340498352363L;

	private Integer id;

	private String loginNum;

	private String loginName;

	private String ipAddress;

	private String methodName;

	private String methodRemark;

	private String operatingContent;

	private String createTm;


	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getLoginNum() {
		return loginNum;
	}

	public void setLoginNum(String loginNum) {
		this.loginNum = loginNum;
	}

	public String getLoginName() {
		return loginName;
	}

	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public String getIpAddress() {
		return ipAddress;
	}

	public void setIpAddress(String ipAddress) {
		this.ipAddress = ipAddress;
	}

	public String getMethodName() {
		return methodName;
	}

	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	public String getMethodRemark() {
		return methodRemark;
	}

	public void setMethodRemark(String methodRemark) {
		this.methodRemark = methodRemark;
	}

	public String getOperatingContent() {
		return operatingContent;
	}

	public void setOperatingContent(String operatingContent) {
		this.operatingContent = operatingContent;
	}

	public String getCreateTm() {
		return createTm;
	}

	public void setCreateTm(String createTm) {
		this.createTm = createTm;
	}
}

(4)日志切入【添加注解标志即可】

/**
     * 删除金融机构
     * @param request
     * @param response
     * @param operateVersionId 机构版本
     * @param orgId 机构iD
     * @param orgLvl 机构层级
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "delOrgByFlagAndOrgId", method = RequestMethod.POST, produces = {"application/json"})
    @MethodLog(remark = "删除机构")
    public String delOrgByFlagAndOrgId(HttpServletRequest request,
                                     HttpServletResponse response,
                                     @RequestParam(value = "operateVersionId", required = false) String operateVersionId,
                                     @RequestParam(value = "orgId", required = false) String orgId,
                                     @RequestParam(value = "orgLvl", required = false) String orgLvl) throws Exception{
        orgService.delOrgByFlagAndOrgId(areaIdPrefix,orgId, orgLvl, operateVersionId);
        return "0";
    }

(5)maven注入aop依赖

<!--spring切面aop依赖-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

在application.properties文件里加这样一条配置
spring.aop.auto=true

在springboot项目里加这两条配置即可,就可以开启aop功能

总结:

  至此,每个重要方法可以开始加注解标志,获取参数,记录关键信息,输出日志了。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值