springboot 集成aop 实现日志

项目例子: https://github.com/nlxs0511/springmybatisplus.git

一、所有方法



import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.eebbk.demo.dao.RequestLogMapper;
import com.eebbk.demo.pojo.RequestLogEntity;
import com.eebbk.demo.utils.PropertiesUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
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.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * <p>ControllerLogAspect</p>
 */
@Aspect
@Component
public class ControllerLogAspect {

    @Resource
    private RequestLogMapper logMapper;

    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAspect.class);


    @Pointcut("execution (* com.eebbk.demo.controller..*.*(..))")
    public void controllerAspect(){

    }

    @Around("controllerAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String logtoDb = PropertiesUtil.findAppConfigByName("request.logtodb");
        if (!org.springframework.util.StringUtils.hasText(logtoDb)) {
            return joinPoint.proceed();
        }

        if(RequestContextHolder.getRequestAttributes() == null){
            return joinPoint.proceed();
        }
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Object[] o = joinPoint.getArgs();
        List<Object> args = o == null ? Collections.emptyList() : Lists.newArrayList(o);
        Iterator<Object> it = args.iterator();

        while (it.hasNext()) {
            Object arg = it.next();
            if (arg instanceof BindingResult) {
                it.remove();
            }
            if (arg instanceof ServletRequest) {
                it.remove();
            }
        }
        long timestamp = System.currentTimeMillis();
        String url = request.getRequestURI();
        String ip = request.getRemoteAddr();
//        由于文件上传日志打印太大,关闭此类接口日志输出
        if(StringUtils.isNoneBlank(url) && url.contains("/searchQuestionByImage")){
            return joinPoint.proceed();
        }
        String methodName = joinPoint.getSignature().toShortString();
        Object resp = joinPoint.proceed();
        long cast = System.currentTimeMillis() - timestamp;
        try {
//            String param = JSON.toJSONString(args,true);
            String param = JSON.toJSONString(args, SerializerFeature.IgnoreErrorGetter);
            RequestLogEntity log = new RequestLogEntity();
            log.setCreateTime(new Date());
            log.setRequestUrl(url);
            log.setParam(param);
            if("true".equalsIgnoreCase(logtoDb)){
                saveLog(log);
            }
            String responseLog = StrUtil.format("请求路径:{},方法:{},参数:{},耗时:{} ms",url,methodName, param,cast);
            LOGGER.info(responseLog);

        } catch (Exception e) {
            //ignore
        }
        return resp;
    }


    private void saveLog(RequestLogEntity log){
        if(log.getRequestUrl().contains("/healthy")){
            return;
        }
        logMapper.insert(log);
    }

}

1.pom.xml

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.自定义log注解类

package com.test.mybatisplus.aop;
import java.lang.annotation.*;

/**
 * @Description 日志注解
 * @project
 * @author:hf
 * @date:
 * @company:人生有限公司
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 要执行的操作类型比如:add操作
     **/
    public String operationType() default "";

    /**
     * 要执行的具体操作比如:添加用户
     **/
    public String operationName() default "";

}

3.SystemLogAspect 

package com.test.mybatisplus.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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 javax.servlet.http.HttpSession;
import java.lang.reflect.Method;

/**
 * @Description〈
 * @project
 * @author:hf
 * @date:
 * @company:人生电子有限公司
 */
@Component
@Aspect
@Slf4j
public class SystemLogAspect {
    //正常 device : -->around-->before-->method-->after-->afterReturn
    //报错 device : -->around-->before-->method-->after-->afterthrowing
    @Pointcut("execution(* com.test.mybatisplus.controller.AopController.*(..))")
    public void controllerAspect() {
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("==========执行controller前置通知===============");
        if (log.isInfoEnabled()) {
            log.info("before " + joinPoint);
            log.info("before start");
            joinPoint.getArgs();//输入的参数列表
            joinPoint.getTarget().getClass().getName();///类全路径
            joinPoint.getSignature().getDeclaringTypeName();//接口全路径
            joinPoint.getSignature().getName();//调用的方法
            log.info("before end");
        }
    }
    /**
     * @Description:配置controller环绕通知,使用在方法aspect()上注册的切入点
     * @Return:
     * @Author: hf
     * @Time: 2021/3/5 15:32
     * @Company 人生有限公司
     */
    @Around("controllerAspect()")
    public Object  around (ProceedingJoinPoint pjp){
        log.info("==========开始执行controller环绕通知===============");
        long start = System.currentTimeMillis();
        Object object=null;
        try {
             object=    pjp.proceed();
            // ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            //日志级别过滤
            if (log.isInfoEnabled()) {
                log.info("around " + pjp + "\tUse time : " + (end - start) + " ms!");
            }
            log.info("==========结束执行controller环绕通知===============");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            if (log.isInfoEnabled()) {
                log.info("around " + pjp + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
        return object;
    }

      /**
       * @Description:
       * @Return:
       * @Author: hf
       * @Time: 2021/3/5 15:32
       * @Company 人生有限公司
       */
        @After("controllerAspect()")
        public void after (JoinPoint joinPoint){
            log.info("=====after=====");
        }
        /**
         * @Description:配置后置返回通知,使用在方法aspect()上注册的切入点
         * @Return:
         * @Author: hf
         * @Time: 2021/3/5 15:32
         * @Company 人生有限公司
         */
        @AfterReturning("controllerAspect()")
        public void afterReturn(JoinPoint joinPoint){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request =attributes.getRequest();
            //读取session中的用户
            HttpSession session = request.getSession();
            //请求的IP
            String ip = request.getRemoteAddr();
            try {
                String targetName = joinPoint.getTarget().getClass().getName();
                String methodName = joinPoint.getSignature().getName();
                Object[] arguments = joinPoint.getArgs();
                Class targetClass = Class.forName(targetName);
                Method[] methods = targetClass.getMethods();
                String operationType = "";
                String operationName = "";
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        Class[] clazzs = method.getParameterTypes();
                        if (clazzs.length == arguments.length) {
                            operationType = method.getAnnotation(Log.class).operationType();
                            operationName = method.getAnnotation(Log.class).operationName();
                            break;
                        }
                    }
                }
                //*========控制台输出=========*//
                log.info("=====controller后置通知开始=====");
                log.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
                log.info("方法描述:" + operationName);
                log.info("请求人:" + "xxx");
                log.info("请求IP:" + ip);
                //*========数据库日志=========*//
                log.info("=====controller后置通知结束=====");
            } catch (Exception e) {
                //记录本地异常日志
                log.error("==后置通知异常==");
                log.error("异常信息:{}", e.getMessage());
            }
        }

        /**
         * @Description:异常通知 用于拦截记录异常日志
         * @Return:
         * @Author: hf
         * @Time: 2021/3/5 15:32
         * @Company 人生有限公司
         */
        @AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
        public void doAfterThrowing (JoinPoint joinPoint, Throwable e){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //读取session中的用户
            HttpSession session = request.getSession();
            String ip = request.getRemoteAddr();
            String params = "";
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    //params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";";
                }
            }
            try {
                String targetName = joinPoint.getTarget().getClass().getName();
                String methodName = joinPoint.getSignature().getName();
                Object[] arguments = joinPoint.getArgs();
                Class targetClass = Class.forName(targetName);
                Method[] methods = targetClass.getMethods();
                String operationType = "";
                String operationName = "";
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        Class[] clazzs = method.getParameterTypes();
                        if (clazzs.length == arguments.length) {
                            operationType = method.getAnnotation(Log.class).operationType();
                            operationName = method.getAnnotation(Log.class).operationName();
                            break;
                        }
                    }
                }
                /*========控制台输出=========*/
                log.info("=====异常通知开始=====");
                log.info("异常代码:" + e.getClass().getName());
                log.info("异常信息:" + e.getMessage());
                log.info("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
                log.info("方法描述:" + operationName);
                log.info("请求人:" + "xxx");
                log.info("请求IP:" + ip);
                log.info("请求参数:" + params);
                /*==========数据库日志=========*/
                //保存数据库
                log.info("=====异常通知结束=====");
            } catch (Exception ex) {
                //记录本地异常日志
                log.error("==异常通知异常==");
                log.error("异常信息:{}", ex.getMessage());
            }
            log.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName());
        }
    }


4.AopController 

package com.test.mybatisplus.controller;

import com.test.mybatisplus.aop.Log;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Description aop测试类
 * @project
 * @author:hf
 * @date:
 * @company:人生有限公司
 */
@Api
@RestController
@RequestMapping("aopController")
@Slf4j
public class AopController {

    /**
     * @Description: 测试方法
     * @Author: hf
     * @Time: 2021/3/5 16:26
     * @Company 人生有限公司
     */
    @GetMapping("/testAOP")
    @ApiOperation(value = "testAOP",notes = "testAOP")
    @Log(operationType="testAOP",operationName="测试接口")
    public String testAOP() {
      /* String a=null;
       a.equals("");*/
       return "测试testAOP接口...";
    }

}

5.执行

6.日志输出

2021-03-05 16:38:40.023 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - ==========开始执行controller环绕通知===============
==========执行controller前置通知===============
2021-03-05 16:38:40.026 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - before execution(String com.test.mybatisplus.controller.AopController.testAOP())
2021-03-05 16:38:40.026 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - before start
2021-03-05 16:38:40.027 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - before end
2021-03-05 16:38:40.040 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - =====controller后置通知开始=====
2021-03-05 16:38:40.040 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - 请求方法:com.test.mybatisplus.controller.AopController.testAOP().testAOP
2021-03-05 16:38:40.040 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - 方法描述:测试接口
2021-03-05 16:38:40.041 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - 请求人:xxx
2021-03-05 16:38:40.041 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - 请求IP:0:0:0:0:0:0:0:1
2021-03-05 16:38:40.042 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - =====controller后置通知结束=====
2021-03-05 16:38:40.044 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - =====after=====
2021-03-05 16:38:40.044 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - around execution(String com.test.mybatisplus.controller.AopController.testAOP())	Use time : 21 ms!
2021-03-05 16:38:40.044 [http-nio-8084-exec-10] INFO  com.test.mybatisplus.aop.SystemLogAspect - ==========结束执行controller环绕通知===============

7.注意:使用AOP的@Around后无返回值

在@Around中执行并返回 object=    pjp.proceed();

  @Around("controllerAspect()")
    public Object  around (ProceedingJoinPoint pjp){
        log.info("==========开始执行controller环绕通知===============");
        long start = System.currentTimeMillis();
        Object object=null;
        try {
             object=    pjp.proceed();
            // ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            //日志级别过滤
            if (log.isInfoEnabled()) {
                log.info("around " + pjp + "\tUse time : " + (end - start) + " ms!");
            }
            log.info("==========结束执行controller环绕通知===============");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            if (log.isInfoEnabled()) {
                log.info("around " + pjp + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
        return object;
    }

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值