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

©️2020 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值