Loading

SpringBoot自定义注解、AOP打印日志

前言

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

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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>

 

自定义注解

1
2
3
4
5
6
7
8
9
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数组,但是数组里只有参数值,没有参数名。还没找到解决办法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@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注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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);
    }
 
}<br><br>

 

切面日志输出效果

 

总结

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

作者:sword-successful

出处:https://www.cnblogs.com/sword-successful/p/10850168.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   歪头儿在北京  阅读(3861)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
阅读排行:
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示