Spring_AOP 記錄系統關鍵操作日志用法


問題:

  系統需要記錄用戶的關鍵操作日志,以便后期的系統維護,方便的查看問題,及時排除

 

分析:

  (1)保存字段:作為一個日志記錄功能,首先數據庫新建一張表保存用戶的操作關鍵字段,

          用戶名,ip,操作描述,時間,日志id

  (2)采用技術:

      第一種:新建一個日志業務實現,在操作發生時進行聯動

          缺點是耦合太緊密,無用代碼增多,后期代碼臃腫,改動時地方分散,不利於維護

      第二種:使用spring 的 aop 技術進行切面切入

          由於本身系統結構不規范,參數,方法名沒有一致的樣式,使用正則匹配方法名不是很方便

          本身日志記錄也只是記錄關鍵操作,api全部切入的話,就不需要這個功能了

          必須有針對性的記錄關鍵操作日志

      第三種:使用spring 的 aop 技術切到自定義注解上,針對不同注解標志進行參數解析,記錄日志

          缺點是要針對每個不同的注解標志進行分別取注解標志,獲取參數進行日志記錄輸出

         

解決:

  由於本身系統的不規范,個人技術能力的欠缺,目前采用第三種辦法進行關鍵日志的記錄

  (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】

package org.triber.portal.service.logAop;

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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.triber.common.helper.StringUtil;
import org.triber.common.helper.UserUtil;
import org.triber.common.model.menu.MenuModel;
import org.triber.common.model.user.User;
import org.triber.portal.dao.mapper.operateLog.OperateLogMapper;
import org.triber.portal.model.area.Area;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * 日志切面實現
 */

@Component
@Aspect
public class LogService {

    @Autowired
    private OperateLogMapper dao;

    public LogService() {
        System.out.println("Aop");
    }

    /**
     * 切點
     */
    @Pointcut("@annotation(org.triber.portal.service.logAop.MethodLog)")
    public void methodCachePointcut() { }


    /**
     * 切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("methodCachePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");
        Calendar ca = Calendar.getInstance();
        String operDate = df.format(ca.getTime());
        String ip = getIp(request);
        User user = UserUtil.getCurrentUser(request);
        String loginName;
        String name;
        if (user != null) {
            loginName = user.getLoginName();
        } else {
            loginName = "匿名用戶";
        }
        String methodRemark = getMthodRemark(point);
        String methodName = point.getSignature().getName();
        String packages = point.getThis().getClass().getName();
        if (packages.indexOf("$$EnhancerByCGLIB$$") > -1) { // 如果是CGLIB動態生成的類
            try {
                packages = packages.substring(0, packages.indexOf("$$"));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        String operatingcontent = "";
        Object[] method_param = null;

        Object object;
        try {
            method_param = point.getArgs(); //獲取方法參數
            // String param=(String) point.proceed(point.getArgs());
            object = point.proceed();
        } catch (Exception e) {
            // 異常處理記錄日志..log.error(e);
            throw e;
        }
        Syslog sysLog = new Syslog();
        sysLog.setOptId(org.triber.common.util.StringUtil.getRandNum(11));
        sysLog.setLoginId(user.getLoginId());
        sysLog.setLoginName(loginName);
        sysLog.setIpAddress(ip);
        sysLog.setMethodName(packages + "." + methodName);
        sysLog.setMethodRemark(methodRemark);
        sysLog.setOptDate(operDate);

        /**
         * 用戶操作
         */
        if("新增用戶".equals(methodRemark)){
            HttpServletRequest req = (HttpServletRequest) method_param[0];
            sysLog.setOperatingcontent("新增用戶: 用戶名為 " + req.getParameter("userName"));

        }
        if("刪除用戶".equals(methodRemark)){
            String loginId = (String) method_param[1];
            sysLog.setOperatingcontent("刪除用戶: 用戶id為 " + loginId);
        }
        if("修改用戶".equals(methodRemark)){
            HttpServletRequest req = (HttpServletRequest) method_param[0];
            sysLog.setOperatingcontent("修改用戶: 用戶名為 " + req.getParameter("loginName"));
        }

        /**
         * 角色操作
         */
        if("新增角色".equals(methodRemark)){
            HttpServletRequest req = (HttpServletRequest) method_param[0];
            sysLog.setOperatingcontent("新增角色: 角色名為 " + req.getParameter("roleDesc"));
        }
        if("刪除角色".equals(methodRemark)){
            String roleId = (String) method_param[1];
            sysLog.setOperatingcontent("新增角色: 角色id為 " + roleId);
        }
        if("修改角色".equals(methodRemark)){
            HttpServletRequest req = (HttpServletRequest) method_param[0];
            sysLog.setOperatingcontent("修改角色: 角色名為 " + req.getParameter("roleDesc"));
        }

        /**
         * 菜單操作
         */
        if("新增菜單".equals(methodRemark)){
            MenuModel menuModel = (MenuModel) method_param[0];
            sysLog.setOperatingcontent("新增菜單: 菜單名為 " + menuModel.getMenuName());
        }
        if("刪除菜單".equals(methodRemark)){
            String menuId = (String) method_param[0];
            sysLog.setOperatingcontent("刪除菜單: 菜單id為 " + menuId);
        }
        if("修改菜單".equals(methodRemark)){
            MenuModel menuModel = (MenuModel) method_param[0];
            sysLog.setOperatingcontent("修改菜單: 菜單名為 " + menuModel.getMenuName());
        }

        /**
         * 部門操作
         */
        if("新增部門".equals(methodRemark)){
            String deptDesc = (String) method_param[1];
            sysLog.setOperatingcontent("新增部門: 部門名為 " + deptDesc);

        }
        if("修改部門".equals(methodRemark)){
            String deptDesc = (String) method_param[2];
            sysLog.setOperatingcontent("修改部門: 部門名為 " + deptDesc);
        }

        /**
         * 地區操作
         */
        if("新增地區".equals(methodRemark)){
            Area area = (Area) method_param[0];
            sysLog.setOperatingcontent("新增地區: 地區名為 " + area.getAreaDscr().trim());
        }
        if("刪除地區".equals(methodRemark)){
            String areaId = (String) method_param[0];
            sysLog.setOperatingcontent("刪除地區: 地區id為 " + areaId);
        }
        if("修改地區".equals(methodRemark)){
            Area area = (Area) method_param[0];
            sysLog.setOperatingcontent("修改地區: 地區名為 " + area.getAreaDscr().trim());
        }

        /**
         * 機構操作
         */
        if("新增機構".equals(methodRemark)){
            String isAdd = (String) method_param[1];
            if(isAdd.equals("1")){
                String addOrgDscrPancy = (String) method_param[5];
                sysLog.setOperatingcontent("新增機構: 機構名為 " + addOrgDscrPancy);
            }else{
                String addOrgDscrPancy = (String) method_param[5];
                sysLog.setOperatingcontent("修改機構: 機構名為 " + addOrgDscrPancy);
            }

        }
        if("刪除機構".equals(methodRemark)){
            String orgId = (String) method_param[3];
            sysLog.setOperatingcontent("刪除機構: 機構id為 " + orgId);
        }

        /**
         * 空參數
         */
        if("".equals(methodRemark) || null == methodRemark){
            sysLog.setOperatingcontent("操作參數: " + method_param[0]);
        }

        dao.saveSysLog(sysLog);
//        System.out.println("日志實體:"+sysLog.getLoginName()+sysLog.getMethodRemark()+sysLog.getOperatingcontent());
        return object;

    }

    /**
     * 方法異常時調用
     *
     * @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)新建日志實體【Syslog】

package org.triber.portal.service.logAop;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

/**
 * 操作日志實體類
 */
public class Syslog implements Serializable{

    @Setter@Getter private String optId;
    @Setter@Getter private String loginId;
    @Setter@Getter private String loginName;
    @Setter@Getter private String ipAddress;
    @Setter@Getter private String methodName;
    @Setter@Getter private String methodRemark;
    @Setter@Getter private String operatingcontent;
    @Setter@Getter private String optDate;
    //模糊條件
    @Setter@Getter private String serchCondition;
}

  (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功能

總結:

  至此,大功告成,可以開始加注解標志,獲取參數,記錄關鍵信息,輸出日志了。。。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM