SpringBoot通過AOP實現系統日誌記錄(Controller層日誌監控,將日誌信息保存到數據庫)

1、
導入pom文件

	<!--用於日誌存儲,不引用打包時會找不到JDBCAppender -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<!--spring切面aop依賴-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!-- 一個工具包 -->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-core</artifactId>
			<version>5.3.7</version>
		</dependency>
		<!-- aop註解 -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.5</version>
		</dependency>

2、設計日誌實體,即設計表結構

package com.gwh.axb.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;

//import lombok.Data;

/**
 * @version V1.0
 * @Package com.gwh.axb.entity
 * @author: gaowenhui
 * @Date: 10:49
 */
//@Data
@Component
public class AdminLog implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer logId;                   //日誌主鍵
    private String type;                     //日誌類型
    private String operation;                 //日誌操作事件描述
    private String remoteAddr;                //請求地址ip
    private String requestUri;                //URI
    private String method;                   //請求方式
    private String params;                   //提交參數
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date operateDate;                    //開始時間
    private Integer userId;                    //用戶ID
    private String userName;                 //用戶名稱
    private String resultParams;            //返回參數
    private String exceptionLog;           //異常描述


    public AdminLog() {
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public Integer getLogId() {
        return logId;
    }

    public void setLogId(Integer logId) {
        this.logId = logId;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getOperation() {
        return operation;
    }

    public void setOperation(String operation) {
        this.operation = operation;
    }

    public String getRemoteAddr() {
        return remoteAddr;
    }

    public void setRemoteAddr(String remoteAddr) {
        this.remoteAddr = remoteAddr;
    }

    public String getRequestUri() {
        return requestUri;
    }

    public void setRequestUri(String requestUri) {
        this.requestUri = requestUri;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getParams() {
        return params;
    }

    public void setParams(String params) {
        this.params = params;
    }

    public Date getOperateDate() {
        return operateDate;
    }

    public void setOperateDate(Date operateDate) {
        this.operateDate = operateDate;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getResultParams() {
        return resultParams;
    }

    public void setResultParams(String resultParams) {
        this.resultParams = resultParams;
    }

    public String getExceptionLog() {
        return exceptionLog;
    }

    public void setExceptionLog(String exceptionLog) {
        this.exceptionLog = exceptionLog;
    }


    public AdminLog(Integer logId, String type, String operation, String remoteAddr, String requestUri, String method, String params, Date operateDate, Integer userId, String userName, String resultParams, String exceptionLog) {
        this.logId = logId;
        this.type = type;
        this.operation = operation;
        this.remoteAddr = remoteAddr;
        this.requestUri = requestUri;
        this.method = method;
        this.params = params;
        this.operateDate = operateDate;
        this.userId = userId;
        this.userName = userName;
        this.resultParams = resultParams;
        this.exceptionLog = exceptionLog;
    }
}

3、自定義註解(此註解需要放在監控的Controller的方法上。)

package com.gwh.axb.config;

import java.lang.annotation.*;

/**
 import java.lang.annotation.*;
 */
@Target({ ElementType.PARAMETER,ElementType.METHOD }) //註解放置的目標位置,METHOD是可註解在方法級別上
@Retention(RetentionPolicy.RUNTIME) //註解在哪個階段執行
@Documented//生成文檔
public @interface SystemControllerLog {
    /** 操作事件     */
    String operation() default "";
    /** 日誌類型 */
    String type();
}

4、切面(攔截)

package com.gwh.axb.config;

import com.gwh.axb.entity.AdminLog;
import com.gwh.axb.service.AddLogService;
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.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.net.InetAddress;
import java.net.UnknownHostException;




/**
 * @version V1.0
 * @Package com.gwh.axb.config
 * @author: gaowenhui
 * @Date: 11:37
 */
@Aspect
@Component
public class SystemLogAspect {
    private static Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
    /**
     * 操作數據庫
     */
    @Autowired
    private AddLogService addLogService;

    /*定義切點
     *  Controller層切點 註解攔截
     */
    @Pointcut("@annotation(com.icbc.axb.config.SystemControllerLog)")
    public void logPointCut() {
    }

    /*切面*/
    @Around(value = "logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {

        logger.info("調用日誌監控");
        AdminLog adminLog = new AdminLog();
        /*從切面值入點獲取植入點方法*/
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        /*獲取切入點方法*/
        Method method = signature.getMethod();
        /*獲取方法上的值*/
        SystemControllerLog systemControllerLog = method.getAnnotation(SystemControllerLog.class);
        /*保存操作事件*/
        if (systemControllerLog != null) {
            String operation = systemControllerLog.operation();

            adminLog.setOperation(operation);
            /*保存日誌類型*/
            adminLog.setOperation(operation);
            String type = systemControllerLog.type();
            adminLog.setType(type);
            /*打印*/
            logger.info("操作事件 :" + operation);
            logger.info("事件類型爲:" + type);
        }
        /*獲取請求體內容*/
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String requestUri = request.getRequestURI();/*獲取請求地址*/
        String requestMethod = request.getMethod();/*獲取請求方式*/
        String remoteAddr1 = request.getRemoteAddr();/*獲取請求IP*/
        String remoteAddr = this.getIpAddress(request);
//        logger.info(remoteAddr1);
//        System.out.println(remoteAddr1 + "處理前的ip-----------" + remoteAddr + "處理後的ip");
        /*存請求地址,請求方式,請求IP*/
        adminLog.setRemoteAddr(remoteAddr);
        logger.info("客戶端IP爲:" + remoteAddr);
        adminLog.setRequestUri(requestUri);
        logger.info("請求路徑爲:" + requestUri);
        adminLog.setMethod(requestMethod);
        logger.info("請求方式爲:" + requestMethod);
        /*獲取參數*/
        Object[] args = joinPoint.getArgs();
        if (args != null) {
            for (Object obj : args) {
                /*   System.out.println("傳遞的參數" + obj);*/
                String params = obj.toString();
                /*      System.out.println("傳遞的參數" + params);*/
                logger.info("請求參數爲:" + params);
                /*保存請求參數*/
                adminLog.setParams(params);
            }
        }

      /*  // 操作人賬號、姓名(需要提前將用戶信息存到session)
        AdminUser adminUser = (AdminUser) request.getSession().getAttribute("adminUser");
        if (adminUser != null) {

            Integer userId = adminUser.getUserId();
//            System.out.println(userId);
            String userName = adminUser.getUserName();
            adminLog.setUserId(userId); *//*存入操作人Id*//*
            adminLog.setUserName(userName); *//*存入操作人名字*//*
            logger.info("操作員是" + userName);
            logger.info("操作員Id爲"+userId);
        }*/
        Object proceed = null;

        try {
            //執行增強後的方法
            proceed = joinPoint.proceed();
           /* System.out.println(proceed+"這是什麼!!!");
            System.out.println("當前方法執行完成");*/
            if (method.isAnnotationPresent(SystemControllerLog.class)) {
                adminLog.setExceptionLog("無異常");
                adminLog.setType("info");
                adminLog.setResultParams(proceed.toString());

            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            adminLog.setExceptionLog(throwable.getMessage());
            adminLog.setType("Err");
            adminLog.setResultParams(proceed.toString());
          /*  System.out.println(throwable.getMessage() + "123456異常信息");
            System.out.println(adminLog.getExceptionLog() + "654321異常信息");
            System.out.println(adminLog);*/

        } finally {
            addLogService.insert(adminLog);
        }
        logger.info("返回參數爲"+proceed);
        return proceed;
    }

    //ip處理工具類
    public String getIpAddress(HttpServletRequest request) {

        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根據網卡取本機配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //對於通過多個代理的情況,第一個IP爲客戶端真實IP,多個IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
}


當前用戶信息在session中就可以保存當前用戶,後面因爲自己覺得不需要就註釋掉了。

5、controller層(只需要加前面自定義的註釋)

    /*測試返回體*/
    @SystemControllerLog(operation = "測試",type = "info")
    @GetMapping("/api/test")
    @ResponseBody
    public String test(){
        String str="相信未來";
        if (str!=null){
            //RUtils是我自己的統一返回體,根據自己情況
            return "str: "+str;
        }else {
            //這是自己的全局異常處理
            //throw new Exception();
            return "str-null: "+str;
        }
    }

運行項目測試
6.1
postman測試

在這裏插入圖片描述

控制檯信息

數據庫

在這裏插入圖片描述

6.2
以上爲無異常。下面爲發生異常時。此處演示異常爲1/0異常。
controller層
/模擬需要登錄後的業務2/
@SystemControllerLog(operation = “測試”,type = “info”)
@GetMapping("/api/findById")
public R findById(){
Student student = studentService.findById();
if(student!=null){
int a=1;
int b=0;
int c=a/b;
return RUtils.success(student);
}else {
throw new StudentException(Renum.TSEST_IS_ERROR);
}
}

postman顯示,此處返回體是自己的異常處理後統一返回體

控制檯顯示(紅色爲異常信息)
在這裏插入圖片描述

數據庫

總結:需要根據自己需求設計表結構。上面省略了日誌信息的添加的地方,就是一套簡單的增加流程。難點應該在於關於AOP切面攔截上面異常。對於IP地址工具類可以提出去單獨放入工具類。
希望給到你一些參考,喜歡請點贊。

示例代碼已傳到github:https://github.com/gwh2008/spring-boot-aop

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章