SpringBoot通过AOP实现系统日志记录(Controller层日志监控,将日志信息保存到数据库)

Posted 清_澈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>LATEST</version>
        </dependency>
        <!-- aop注解 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>`

2、设计日志实体,即设计表结构

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

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


@Data

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;           //异常描述
    }


3、自定义注解(此注解需要放在监控的Controller的方法上。)
 

import java.lang.annotation.*;


@Target({ ElementType.PARAMETER,ElementType.METHOD }) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented//生成文档
public @interface SystemControllerLog {
    /** 操作事件     */
    String operation() default "";
    /** 日志类型 */
    String type();
}

4、切面(拦截)

import com.ss.jwt.annotation.SystemControllerLog;
import com.ss.jwt.pojo.AdminLog;
import com.ss.jwt.pojo.AdminUser;
import com.ss.jwt.service.AddLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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;


@Aspect
@Component
public class SystemLogAspect {
    private static Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
    /**
     * 操作数据库
     */
    @Autowired
    private AddLogService addLogService;

    /*定义切点
     *  Controller层切点 注解拦截  
     */
    @Pointcut("@annotation(com.ss.jwt.annotation.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.addLog(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 R test(){
        String str="相信未来";
        if (str!=null){
        //RUtils是我自己的统一返回体,根据自己情况
            return RUtils.success(str);
        }else {
        //这是自己的全局异常处理
            throw new StudentException(Renum.TSEST_IS_ERROR);
        }
    }
   

运行项目测试
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地址工具类可以提出去单独放入工具类。
希望给到你一些参考,喜欢请点赞。
 

以上是关于SpringBoot通过AOP实现系统日志记录(Controller层日志监控,将日志信息保存到数据库)的主要内容,如果未能解决你的问题,请参考以下文章

springboot—spring aop 实现系统操作日志记录存储到数据库

SpringBoot应用中使用AOP记录接口访问日志

springboot-使用AOP日志拦截实现

Springboot接口项目利用AOP记录日志

Springboot接口项目利用AOP记录日志

springboot-aop面向切面编程