zoukankan      html  css  js  c++  java
  • SpringBoot自定义注解、AOP打印日志

    前言

           在SpringBoot中使用自定义注解、aop切面打印web请求日志。主要是想把controller的每个request请求日志收集起来,调用接口、执行时间、返回值这几个重要的信息存储到数据库里,然后可以使用火焰图统计接口调用时长,平均响应时长,以便于我们对接口的调用和执行情况及时掌握。

    添加依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>2.1.4.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <version>2.1.4.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
          <version>2.1.4.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>com.google.code.gson</groupId>
          <artifactId>gson</artifactId>
          <version>2.8.5</version>
        </dependency>
    

     

    自定义注解

    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    @Documented
    public @interface WebLogger {
    
        String value() default "";
    }
    

      

    AOP定义切面、切点

          在实现切面之前先要了解几个aop的注解:

    •  @Aspect、
    •  @Pointcut 定义切点,后面跟随一个表达式,表达式可以是一个注解,也可以具体到方法级别。
    •  @Before
    •  @After
    •  @Around

          实现切面,在before方法里统计request请求相关参数,在around方法里计算接口调用时间。这里统计请求的参数时有个bug,joinpoint.getArgs返回值是一个Object数组,但是数组里只有参数值,没有参数名。还没找到解决办法。

    @Aspect
    @Component
    public class WebLoggerAspect {
    
        @Pointcut("@annotation(com.zhangfei.anno.WebLogger)"
        public void log() {}
    
        @Around("log()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            long startTime=System.currentTimeMillis();
    
            Object result=joinPoint.proceed();
            System.out.println("Response:"+new Gson().toJson(result));
            System.out.println("耗时:"+(System.currentTimeMillis()-startTime));
    
            return result;
        }
    
        @Before("log()")
        public void doBefore(JoinPoint joinPoint) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
    
            System.out.println("==================Start=================");
            System.out.println("URL:" + request.getRequestURL().toString());
            System.out.println("Description:" + getLogValue(joinPoint));
            System.out.println("Method:" + request.getMethod().toString());
    
            //打印controller全路径及method
            System.out.println("Class Method:" + joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
            System.out.println("客户端IP:" + request.getRemoteAddr());
    
            System.out.println("请求参数:" + new Gson().toJson(joinPoint.getArgs()));
    
        }
    
        private String getLogValue(JoinPoint joinPoint) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
    
            WebLogger webLogger = method.getAnnotation(WebLogger.class);
    
            return webLogger.value();
        }
    
        @After("log()")
        public void doAfter() {
            System.out.println("==================End=================");
        }
    } 

    怎么使用注解?

          这里就直接在你的web请求方法上直接添加WebLogger注解

    @Controller
    @RequestMapping("/student")
    public class StudentController {
    
        private final Logger logger= LoggerFactory.getLogger(StudentController.class);
    
        @GetMapping("/list")
        @WebLogger("学生列表")
        public @ResponseBody  List<Student> list(){
            ArrayList<Student> list=new ArrayList<>();
            Student student0=new Student(1,"kobe",30);
            Student student1=new Student(2,"james",30);
            Student student2=new Student(3,"rose",30);
    
            list.add(student0);
            list.add(student1);
            list.add(student2);
    
            return list;
        }
    
        @WebLogger("学生实体")
        @RequestMapping("/detail")
        public @ResponseBody Student student(int id){
            return new Student(1,"kobe",30);
        }
    
    }

    切面日志输出效果

    总结

            从头条上一位朋友文章评论上看到有人提出,在分布式部署的环境中,出现高并发时日志打印会出现错乱的情况,这里还需要把线程id或者声明一个guid, 存入ThreadLoal中统计使用。当然更多的人简历还是使用ELK等分布式日志解决方法,好吧,这些还有待于自己部署环境去尝试。

  • 相关阅读:
    软件测试作业1:android手机应用布局之TabActivity
    软件测试作业2:对faulty,error和failure的理解和应用
    python-字符串常用方法、文件简单读写
    python-字典练习题
    python-字典
    python-list 列表 数组
    python基础一循环
    Charles抓包
    Jmeter分布式
    Jmeter如何操作数据库
  • 原文地址:https://www.cnblogs.com/sword-successful/p/10850168.html
Copyright © 2011-2022 走看看