登录
首页 >  文章 >  java教程

Java怎么在CompletableFuture中实现日志记录

来源:亿速云

时间:2024-04-27 13:09:36 253浏览 收藏

小伙伴们有没有觉得学习文章很有意思?有意思就对了!今天就给大家带来《Java怎么在CompletableFuture中实现日志记录》,以下内容将会涉及到,若是在学习中对其中部分知识点有疑问,或许看了本文就能帮到你!

1.首先利用aop为所有请求入口添加requestId。

@Aspect
@Component
public class LoggingAspect {

    /**
     * AOP注解的Controller类方法必须为 public 或 protect ,千万不能用private!!!!!!!!否则会@Autowired注入的service会报空指针异常。
     * 私有方法和字段不属于Spring上下文中的bean属性。
     */
    @Around("@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 在logback-spring.xml里对应%X{requestId}
        MDC.put("requestId", UUID.randomUUID().toString().substring(0, 13));  // Add request ID to MDC
        try {
            return joinPoint.proceed(); // Execute method
        } finally {
            MDC.remove("requestId");  // Remove request ID from MDC
        }
    }
}

2.定义一下 logback-spring.xml ,引入 requestId 来进行链路记录,关键代码是 %X{requestId}



    
    
    
    
    
    
    
    
        
            
                debug
            
            
            ${CONSOLE_LOG_PATTERN}
            
            UTF-8
        
    


    
    
    
    
    
        ${LOG_FILE_PATH}/debug.log 
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} %X{requestId} [%thread] %-5level %logger{50} - %msg%n
            UTF-8
        
        
        
            
            ${LOG_FILE_PATH}/debug-%d{yyyy-MM-dd}.%i.log
            
            
                100MB
            
            
            15
            
            1GB
        
        
        
            debug
            ACCEPT
            DENY
        
    

    
    
        ${LOG_FILE_PATH}/info.log
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} %X{requestId} [%thread] %-5level %logger{50} - %msg%n
            UTF-8
        
        
            ${LOG_FILE_PATH}/info-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            15
        
        
        
            info
            ACCEPT
            DENY
        
    

    
    
        ${LOG_FILE_PATH}/warn.log
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} %X{requestId} [%thread] %-5level %logger{50} - %msg%n
            UTF-8 
        
        
            ${LOG_FILE_PATH}/warn-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            15
        
        
        
            warn
            ACCEPT
            DENY
        
    

    
    
        ${LOG_FILE_PATH}/error.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} %X{requestId} [%thread] %-5level %logger{50} - %msg%n
            UTF-8
        
        
            ${LOG_FILE_PATH}/error-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            15
        
        
        
            ERROR
            ACCEPT
            DENY
        
    

    
    
        
    
    
        
    
    
        
    
    
        
    

    
    
        
        
        
        
        
    


2.定义一个`复杂`业务流程,来看看日志的实力

private final ExecutorService executorService = Executors.newFixedThreadPool(4);

@GetMapping("saveUser")
    public String saveUser() {
    log.info("进入了saveUser");
    // 异步
    CompletableFuture.runAsync(()-> b(), executorService);
    log.info("退出了saveUser");
    return "Ok";
}

private void b() {
    log.info("进入了b");
}

日志如下,可以看到异步线程的requestId丢失了,21dbaad3-3158 这个就是requestId,这种情况下我们需要自定义线程类来保存MDC的上下文

2023-04-19 11:51:59.309 21dbaad3-3158  INFO 23044 --- [p-nio-80-exec-1] c.h.m.api.CompletableFutureApi           : 进入了saveUser
2023-04-19 11:51:59.312 21dbaad3-3158  INFO 23044 --- [p-nio-80-exec-1] c.h.m.api.CompletableFutureApi           : 退出了saveUser
2023-04-19 11:51:59.312   INFO 23044 --- [pool-1-thread-1] c.h.m.api.CompletableFutureApi           : 进入了b

3.定义线程实现类,并且在构造函数中存储MDC的上下文

public static class MdcTaskWrapper implements Runnable {
    private final Runnable task;
    private final Map contextMap;

    public MdcTaskWrapper(Runnable task) {
        this.task = task;
        this.contextMap = MDC.getCopyOfContextMap();
    }

    @Override
    public void run() {
        if (contextMap != null) {
            MDC.setContextMap(contextMap);
        }
        try {
            task.run();
        } finally {
            MDC.clear();
        }
    }
}

4.接下来再改写一下runAsync的使用方式,我们用MdcTaskWrapper来进行线程操作,这个线程类是包含mdc上下文的

@GetMapping("saveUser")
public String saveUser() {
    log.info("进入了saveUser");
    // 异步
    CompletableFuture.runAsync(this::b, command -> executorService.execute(new MdcTaskWrapper(command)));
    log.info("退出了saveUser");
    return "Ok";
}

private void b() {
    log.info("进入了b");
}

可以看到,requestId:4ab037ab-92cb,异步线程能够拿到MDC的上下文,并且成功记录链路日志

2023-04-19 11:58:27.581 4ab037ab-92cb  INFO 6816 --- [p-nio-80-exec-5] c.h.m.api.CompletableFutureApi           : 进入了saveUser
2023-04-19 11:58:27.582 4ab037ab-92cb  INFO 6816 --- [p-nio-80-exec-5] c.h.m.api.CompletableFutureApi           : 退出了saveUser
2023-04-19 11:58:27.582 4ab037ab-92cb  INFO 6816 --- [pool-1-thread-1] c.h.m.api.CompletableFutureApi    

本篇关于《Java怎么在CompletableFuture中实现日志记录》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于文章的相关知识,请关注golang学习网公众号!

声明:本文转载于:亿速云 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>