zoukankan      html  css  js  c++  java
  • SpingBoot日志切面(使用自定义注解打印日志)

    使用Sping AOP切面打印日志时,为了不影响之前的代码,可以不拦截全部的controller层接口,而使用时注解的形式,在相应的接口方法加上日志注解,就可以打印请求参数和请求结果信息。

    代码如下:

    1.定义切面类

      1 @Aspect
      2 @Component
      3 @Slf4j
      4 public class LogAspect {
      5 
      6     private LogModel logModel;
      7 
      8     /**
      9      * 这里我们使用注解的形式
     10      * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     11      * 切点表达式:   execution(...)
     12      */
     13     @Pointcut("@annotation(com.zto.poseidon.datacenter.common.annotation.OperationLog)")
     14     public void restControllerLog() {
     15     }
     16 
     17     /**
     18      * 需要忽略的返回日志
     19      */
     20     @Pointcut("!@annotation(com.zto.poseidon.datacenter.common.annotation.IgnoreResponseLog)")
     21     public void ignoreResponseLog() {
     22     }
     23 
     24     @Before("restControllerLog()")
     25     public void exBefore(JoinPoint joinPoint) {
     26         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
     27         HttpServletRequest request = attributes.getRequest();
     28         logModel = new LogModel();
     29         logModel.setUrl(request.getRequestURL().toString());
     30         logModel.setReqIp(request.getRemoteAddr());
     31         logModel.setStartTime(DateTime.now().toDate());
     32         logModel.setMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
     33         try {
     34             logModel.setParams(getRequestParamsByJoinPoint(joinPoint));
     35             log.info("--------------请求参数日志------------");
     36             log.info(" ---> 请求参数 :{}", JSON.toJSONString(logModel));
     37         } catch (Exception e) {
     38             log.info("异常信息:{}", ExceptionUtils.getStackTrace(e));
     39         }
     40     }
     41 
     42     @After("restControllerLog()")
     43     public void exAfter(JoinPoint joinPoint) {
     44         log.info(" ---> request method:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + " 执行完毕!");
     45     }
     46 
     47     @AfterReturning(returning = "result", pointcut = "restControllerLog()  && ignoreResponseLog()")
     48     public void exAfterReturning(Object result) {
     49         log.info("--------------请求结果日志------------");
     50         log.info(" ---> 请求结果 :{}", JSON.toJSONString(result));
     51     }
     52 
     53     private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
     54         //参数名
     55         String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
     56         //参数值
     57         Object[] paramValues = joinPoint.getArgs();
     58         return buildRequestParam(paramNames, paramValues);
     59     }
     60 
     61     private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
     62         Map<String, Object> requestParams = new HashMap<>();
     63         for (int i = 0; i < paramNames.length; i++) {
     64             Object value = paramValues[i];
     65             //如果是文件对象
     66             if (value instanceof MultipartFile) {
     67                 MultipartFile file = (MultipartFile) value;
     68                 //获取文件名
     69                 value = file.getOriginalFilename();
     70             }
     71             requestParams.put(paramNames[i], value);
     72         }
     73         return requestParams;
     74     }
     75 
     76     @Data
     77     public static class LogModel {
     78 
     79         private String url;
     80 
     81         private Date startTime;
     82 
     83         private Date endTime;
     84 
     85         private Long tookTime;
     86 
     87         private Object result;
     88 
     89         private String reqIp;
     90 
     91         private Boolean success = Boolean.FALSE;
     92 
     93         private String errMsg;
     94 
     95         private Object queryString;
     96 
     97         private String method;
     98 
     99         private Map<String, Object> params;
    100 
    101         private RuntimeException exception;
    102 
    103     }
    104 
    105 }

    2.添加自定义日志打印注解,打印入参和出参日志

    1 @Target({ElementType.METHOD, ElementType.TYPE})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface OperationLog {
    4 }

    3.忽视请求结果日志打印注解

    1 @Target({ElementType.METHOD, ElementType.TYPE})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface IgnoreResponseLog {
    4     boolean required() default true;
    5 }

    4.然后在需要打印的接口方法上添加 @OperationLog 注解就ok了

     自定义注解@OperationLog 注解定义了可以使用在类和方法上,但使用在类上不生效。

    看官方文档解释:

    //@Around("@annotation(自定义注解)")//自定义注解标注在方法上的方法执行aop方法
    如:@Around("@annotation(org.springframework.transaction.annotation.Transactional)")

    //@Around("@within(自定义注解)")//自定义注解标注在的类上;该类的所有方法(不包含子类方法)执行aop方法
    如:@Around("@within(org.springframework.transaction.annotation.Transactional)")

    //@Around("within(包名前缀.*)")//com.aop.within包下所有类的所有的方法都会执行(不包含子包) aop方法
    如:@Around("within(com.aop.test.*)")

    //@Around("within(包名前缀..*)")//com.aop.within包下所有的方法都会执行(包含子包)aop 方法
    如:@Around("within(com.aop.test..*)")

    //@Around("this(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,但不包含父类的方法)都会执行aop方法
    如:@Around("this(com.aop.service.TestService)")

    //@Around("target(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,包含父类的方法)
    如:@Around("this(com.aop.service.TestService)")

    //@Around("@target(自定义注解)")//springboot项目启动报如下错误,没有解决
    // Caused by: java.lang.IllegalStateException:
    // StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start

    所以,如果想在类上使用自定义注解,需要将 LogAspect.class 切面类里的

    1 @Pointcut("@annotation(com.zto.poseidon.datacenter.common.annotation.OperationLog)")
    2     public void restControllerLog() {
    3     }

    改为:

    1 @Pointcut("@within(com.zto.poseidon.datacenter.common.annotation.OperationLog)")
    2     public void restControllerLog() {
    3     }

    这样就可以使用在类上了,但是我经过测试发现,改了之后只能用在类上,用在方法上就失效了,不能正常打印日志。暂时没找到原因,如果有找到原因的朋友,可以留言评论一下。

  • 相关阅读:
    springclould feign客户端添加全局参数
    mysql单向自动同步
    MongoDB的安装和配置(Windows系统)及遇到的常见问题解答
    电脑中安装多个jdk,eclipse的选择!
    css(外部样式表)中各种选择器(定义属性时)的优先级
    HTML5结合CSS的三种方法+结合JS的三种方法
    HTML5图片居中的问题
    html->html5->css->javascript(js)->jQuery->AJAX->JSON
    自定义方法实现ArrayList排序
    java,while循环的使用,接收用户的输入,进行不同的操作!
  • 原文地址:https://www.cnblogs.com/linliquan/p/15675368.html
Copyright © 2011-2022 走看看