本篇主要分享的是springboot中结合aop方式来记录请求参数和响应的数据信息;这里主要讲解两种切入点方式,一种方法切入,一种注解切入;首先创建个springboot测试工程并通过maven添加如下依赖:
<!-- AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--阿里 FastJson依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.44</version> </dependency>
先来说方法的切点方式,需要创建个名为LogAspect的组件类,然后用@Aspect注解修饰组件类,再通过设置方法切入点方式做公共日志记录,如下创建切入点:
//切点入口 Controller包下面所有类的所有方法 private final String pointcut = "execution(* com.platform.Controller..*(..))"; //切点 @Pointcut(value = pointcut) public void log() { }
这里的execution( com.platform.Controller..(..))主要的意思是:切入点入口是Controller包下面所有类的所有方法;再来通过@Around环绕注解方法里面做请求参数和响应信息的记录,如下代码:
@Around(value = "log()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result = null; StringBuilder sbLog = new StringBuilder("\n"); try { sbLog.append(String.format("类名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName())); MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); sbLog.append(String.format("方法:%s\r\n", methodSignature.getMethod().getName())); Object[] args = proceedingJoinPoint.getArgs(); for (Object o : args) { sbLog.append(String.format("参数:%s\r\n", JSON.toJSON(o))); } long startTime = System.currentTimeMillis(); result = proceedingJoinPoint.proceed(); long endTime = System.currentTimeMillis(); sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result))); sbLog.append(String.format("耗时:%ss", endTime - startTime)); } catch (Exception ex) { sbLog.append(String.format("异常:%s", ex.getMessage())); } finally { logger.info(sbLog.toString()); } return result; }
此刻主要代码就完成了,再来我们配置下日志的记录方式;首先在 resources目录增加名为logback-spring.xml的文件,其配置信息如:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 3 ~ Author:shenniu003 4 ~ Copyright (c) 2018. 5 --> 6 7 <configuration debug="false" scan="true" scanPeriod="1 seconds"> 8 9 <springProperty scope="context" name="appname" source="logging.logback.appname"/> 10 <springProperty scope="context" name="logLevel" source="logging.logback.level"/> 11 <springProperty scope="context" name="logPath" source="logging.logback.path"/> 12 13 <property name="logPathAll" value="${logPath}/${appname}.log"/> 14 15 <contextName>logback</contextName> 16 17 <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> 18 <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter" > 19 <level>WARN</level> 20 </filter>--> 21 <encoder> 22 <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern> 23 </encoder> 24 </appender> 25 26 <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> 27 <file>${logPathAll}</file> 28 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 29 <fileNamePattern>${logPathAll}.%d{yyyy-MM-dd}.zip</fileNamePattern> 30 </rollingPolicy> 31 <encoder> 32 <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n 33 </pattern> 34 </encoder> 35 </appender> 36 37 <root level="${logLevel}"> 38 <appender-ref ref="console"/> 39 <appender-ref ref="file"/> 40 </root> 41 42 </configuration>
然后application.yml的配置信息如:
1 logging: 2 config: classpath:logback-spring.xml 3 logback: 4 level: info #info ,debug 5 path: /home/app/data/applogs/weblog 6 appname: web
此刻日志和公共的aop记录类都完成了,我们需要创建个测试用例,其代码如:
1 @PostMapping("/addUser") 2 public ResponseEntity<MoStudent> addUser(@RequestBody MoStudent moStudent) throws Exception { 3 moStudent.setNumber(UUID.randomUUID().toString()); 4 // throw new Exception("错误了"); 5 return new ResponseEntity<>(moStudent, HttpStatus.OK); 6 }
最后,通过postman模拟post请求,能够得到如下的日志结果:
上面的方式切入点是所有方法,所有方法都记录日志可能有是不是需求想要的,此时可以通过注解的方式来标记想记录日志的方法;先来创建个日志注解:
1 @Target(ElementType.METHOD) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 public @interface LogAnnotation { 5 /** 6 * 描述 7 * 8 * @return 9 */ 10 String des() default ""; 11 }
同样再来创建注解的切入点:
1 //匹配方法上包含此注解的方法 2 private final String annotationPointCut = "@annotation(com.platform.Aop.LogAnnotation)"; 3 4 //注解切点 5 @Pointcut(value = annotationPointCut) 6 public void logAnnotation() { 7 }
再通过@Around注解绑定具体的操作方法:
1 @Around(value = "logAnnotation()") 2 public Object aroundAnnotation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { 3 Object result = null; 4 StringBuilder sbLog = new StringBuilder("\n"); 5 try { 6 sbLog.append(String.format("类名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName())); 7 8 MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); 9 Method method = methodSignature.getMethod(); 10 LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); 11 if (logAnnotation != null && !logAnnotation.des().isEmpty()) { 12 sbLog.append(String.format("说明:%s\r\n", logAnnotation.des())); 13 } 14 sbLog.append(String.format("方法:%s\r\n", method.getName())); 15 16 Object[] args = proceedingJoinPoint.getArgs(); 17 for (Object o : args) { 18 sbLog.append(String.format("参数:%s\r\n", JSON.toJSON(o))); 19 } 20 21 long startTime = System.currentTimeMillis(); 22 result = proceedingJoinPoint.proceed(); 23 long endTime = System.currentTimeMillis(); 24 sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result))); 25 sbLog.append(String.format("耗时:%ss", endTime - startTime)); 26 } catch (Exception ex) { 27 sbLog.append(String.format("异常:%s", ex.getMessage())); 28 } finally { 29 logger.info(sbLog.toString()); 30 } 31 return result; 32 }
这个方法里需要注意的是注解方式相比第一种其实就多了如下几行代码:
1 Method method = methodSignature.getMethod(); 2 LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); 3 if (logAnnotation != null && !logAnnotation.des().isEmpty()) { 4 sbLog.append(String.format("说明:%s\r\n", logAnnotation.des())); 5 }
下面是注解测试用例:
1 @LogAnnotation(des = "注解记录日志") 2 @PostMapping("/addUser01") 3 public ResponseEntity<MoStudent> addUser01(@RequestBody MoStudent moStudent) throws Exception { 4 moStudent.setNumber(UUID.randomUUID().toString()); 5 return new ResponseEntity<>(moStudent, HttpStatus.OK); 6 }
如下是LogAspect.java的所有代码:
1 /* 2 * Author:shenniu003 3 * Copyright (c) 2018. 4 */ 5 6 package com.platform.Aop; 7 8 import com.alibaba.fastjson.JSON; 9 import org.aspectj.lang.ProceedingJoinPoint; 10 import org.aspectj.lang.annotation.*; 11 import org.aspectj.lang.reflect.MethodSignature; 12 import org.slf4j.Logger; 13 import org.slf4j.LoggerFactory; 14 import org.springframework.stereotype.Component; 15 16 import java.lang.reflect.Method; 17 18 /** 19 * Created by Administrator on 2018/11/5. 20 * springboot+aop切点记录请求和响应信息 21 */ 22 @Component 23 @Aspect 24 public class LogAspect { 25 26 //切点入口 Controller包下面所有类的所有方法 27 private final String pointcut = "execution(* com.platform.Controller..*(..))"; 28 29 //匹配方法上包含此注解的方法 30 private final String annotationPointCut = "@annotation(com.platform.Aop.LogAnnotation)"; 31 32 private Logger logger = LoggerFactory.getLogger(LogAspect.class); 33 34 //切点 35 @Pointcut(value = pointcut) 36 public void log() { 37 } 38 39 @Around(value = "log()") 40 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { 41 Object result = null; 42 StringBuilder sbLog = new StringBuilder("\n"); 43 try { 44 sbLog.append(String.format("类名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName())); 45 46 MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); 47 sbLog.append(String.format("方法:%s\r\n", methodSignature.getMethod().getName())); 48 49 Object[] args = proceedingJoinPoint.getArgs(); 50 for (Object o : args) { 51 sbLog.append(String.format("参数:%s\r\n", JSON.toJSON(o))); 52 } 53 54 long startTime = System.currentTimeMillis(); 55 result = proceedingJoinPoint.proceed(); 56 long endTime = System.currentTimeMillis(); 57 sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result))); 58 sbLog.append(String.format("耗时:%ss", endTime - startTime)); 59 } catch (Exception ex) { 60 sbLog.append(String.format("异常:%s", ex.getMessage())); 61 } finally { 62 logger.info(sbLog.toString()); 63 } 64 return result; 65 } 66 67 //注解切点 68 @Pointcut(value = annotationPointCut) 69 public void logAnnotation() { 70 } 71 72 @Around(value = "logAnnotation()") 73 public Object aroundAnnotation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { 74 Object result = null; 75 StringBuilder sbLog = new StringBuilder("\n"); 76 try { 77 sbLog.append(String.format("类名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName())); 78 79 MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); 80 Method method = methodSignature.getMethod(); 81 LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); 82 if (logAnnotation != null && !logAnnotation.des().isEmpty()) { 83 sbLog.append(String.format("说明:%s\r\n", logAnnotation.des())); 84 } 85 sbLog.append(String.format("方法:%s\r\n", method.getName())); 86 87 Object[] args = proceedingJoinPoint.getArgs(); 88 for (Object o : args) { 89 sbLog.append(String.format("参数:%s\r\n", JSON.toJSON(o))); 90 } 91 92 long startTime = System.currentTimeMillis(); 93 result = proceedingJoinPoint.proceed(); 94 long endTime = System.currentTimeMillis(); 95 sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result))); 96 sbLog.append(String.format("耗时:%ss", endTime - startTime)); 97 } catch (Exception ex) { 98 sbLog.append(String.format("异常:%s", ex.getMessage())); 99 } finally { 100 logger.info(sbLog.toString()); 101 } 102 return result; 103 } 104 }