当前位置: 首页 > article >正文

线程级耗时统计工具类TimeWatcher

线程级耗时统计工具类TimeWatcher

先看效果

  • 假设我们的业务代码逻辑是这样的
    在这里插入图片描述

  • 那么最终的日志打印效果为(注:此为美化输出,也可设置为常规一行输出,还可自定义)

    2024-11-08T23:48:53.390+08:00  INFO 31472 --- [nio-8080-exec-1] c.i.c.filter.TimeWatcherFilter           : consume time -> 
    1....................................987ms @ request-uri -> /user-info
    |--1.1...............................964ms @ TestController#userInfo
    |--|--1.1.1..........................961ms @ 查询用户
    |--|--|--1.1.1.1.....................158ms @ 查询用户基本信息
    |--|--|--1.1.1.2......................84ms @ 查询用户安全信息
    |--|--|--1.1.1.3.....................171ms @ 查询用户拥有的角色
    |--|--|--1.1.1.4.....................206ms @ 根据角色查询api权限
    |--|--|--|--1.1.1.4.1.................53ms @ 查询角色关联的菜单
    |--|--|--|--1.1.1.4.2................152ms @ 查询菜单关联的api权限
    |--|--|--1.1.1.5.....................125ms @ 查询用户直接关联的api权限
    |--|--|--1.1.1.6.....................156ms @ 组装数据
    |--|--|--1.1.1.7......................56ms @ 加密数据
    

工具类获取方式一:使用作者的常用工具包

提示:如果你不想引入作者的工具包,那么你可以直接看获取方式二

第一步:引入作者的常用工具包
<!-- 作者常用工具包 -->
<dependency>
    <groupId>com.idea-aedi</groupId>
    <artifactId>common-spring</artifactId>
    <version>2100.10.7.LTS17</version> <!--jdk17-->
    <!--<version>2100.10.7.LTS8</version>--> <!--jdk8-->
</dependency>

<!-- aop(因为作者还封装了个注解进行支持,要用注解的话需要引入aop) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:启用耗时统计器
/**
 * 开启耗时监控
 * <pre>
 * 注:默认情况下,会:
 *    1.对所有被{@link WatchTime}注解标注的方法进行耗时统计
 *    2.对所有Controller类下的方法进行耗时统计
 *    
 *    当然这些你都可以通过在这里通过指定{@link TimeWatcher}注解的相关属性来进行修改
 * </pre>
 */
@EnableFeature(
        enableTimeWatcher = @TimeWatcher 
)
@SpringBootApplication
public class StartUp {
    
    public static void main(String[] args) {
        SpringApplication.run(StartUp.class, args);
    }
}
第三步:使用耗时器统计器
  • 使用方式一:通过注解(示例)
     @WatchTime(taskName = "查询用户拥有的角色")
     public Collection<Long> queryRole(Long userId) {
         ...
     }
    
  • 使用方式二:使用工具类(示例)
    // 示例一
    try {
        WatchTimeUtil.start("查询用户基本信息");
        mockLogic();
    } finally {
        WatchTimeUtil.stop();
    }
    
    // 示例二
    WatchTimeUtil.voidWatch("查询用户安全信息", () -> mockLogic()); // 方法无返回值
    //Object returnVal = WatchTimeUtil.watch("查询用户安全信息", () -> mockLogic()); // 方法有返回值
    

获取方式二:直接使用工具类

第一步:复制此核心工具类进自己的项目
import com.alibaba.fastjson2.JSON;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;


/**
 * 耗时统计工具
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2100.10.7.LTS17
 */
public final class WatchTimeUtil {
    
    /** 初始化标识 */
    private static final ThreadLocal<Boolean> initThreadLocal = ThreadLocal.withInitial(() -> false);
    
    /** 存放执行中任务的ThreadLocal */
    private static final ThreadLocal<LinkedList<TaskInfo>> runningTaskThreadLocal = new ThreadLocal<>();
    
    /** 存放已结束任务的ThreadLocal */
    private static final ThreadLocal<LinkedList<TaskInfo>> endTaskThreadLocal = new ThreadLocal<>();
    
    /** 存放最大序号的ThreadLocal */
    private static final ThreadLocal<String> maxTaskNoThreadLocal = new ThreadLocal<>();
    
    /**
     * 管理器
     */
    public static class Manager {
    
        /**
         * 初始化
         */
        public static void init() {
            if (initThreadLocal.get()) {
                throw new IllegalStateException("It has already been initialized, please do not initialize it repeatedly.");
            }
            initThreadLocal.set(true);
            runningTaskThreadLocal.set(new LinkedList<>());
            endTaskThreadLocal.set(new LinkedList<>());
        }
    
        /**
         * 初始化
         */
        public static void initSilence() {
            if (initThreadLocal.get()) {
                return;
            }
            initThreadLocal.set(true);
            runningTaskThreadLocal.set(new LinkedList<>());
            endTaskThreadLocal.set(new LinkedList<>());
        }
    
        /**
         * 所有耗时统计任务当前是否都已结束
         */
        public static boolean currIsEnd() {
            LinkedList<TaskInfo> list = runningTaskThreadLocal.get();
            return list == null || list.size() == 0;
        }
    
        /**
         * 获取已完成的任务集合
         */
        public static List<TaskInfo> getEndTaskList() {
            return new ArrayList<>(endTaskThreadLocal.get());
        }
    
        /**
         * 获取进行中的任务集合
         */
        public static List<TaskInfo> getIngTaskList() {
            return new ArrayList<>(runningTaskThreadLocal.get());
        }
        
        /**
         * 清空
         */
        public static void clear() {
            runningTaskThreadLocal.remove();
            endTaskThreadLocal.remove();
            maxTaskNoThreadLocal.remove();
            initThreadLocal.remove();
        }
        
        /**
         * 获取下一个执行序号
         */
        public static String nextNo() {
            TaskInfo taskInfo = runningTaskThreadLocal.get().peekFirst();
            String maxNo = maxTaskNoThreadLocal.get();
            String nextNo;
            if (taskInfo == null) {
                if (maxNo == null) {
                    nextNo = "1";
                } else {
                    nextNo = String.valueOf(Long.parseLong(maxNo.split("\\.")[0]) + 1);
                }
            } else {
                Objects.requireNonNull(maxNo, "maxNo should not be null.");
                String parentNo = taskInfo.getTaskNo();
                String[] parentNoArr = parentNo.split("\\.");
                String[] maxNoArr = maxNo.split("\\.");
                long subNo;
                if (maxNoArr.length > parentNoArr.length) {
                    subNo = Long.parseLong(maxNoArr[parentNoArr.length]) + 1;
                } else {
                    subNo = 1;
                }
                nextNo = parentNo + "." + subNo;
            }
            return nextNo;
        }
    
        /**
         * 按照任务序号排序
         */
        public static void sortByTaskNo(List<TaskInfo> taskInfoList) {
            if (taskInfoList == null) {
                return;
            }
            taskInfoList.sort(
                    (x, y) -> {
                        String xNo = x.getTaskNo();
                        String yNo = y.getTaskNo();
                        String[] xNoArr = xNo.split("\\.");
                        String[] yNoArr = yNo.split("\\.");
                        int minLength = Math.min(xNoArr.length, yNoArr.length);
                        for (int i = 0; i < minLength; i++) {
                            long xVal = Long.parseLong(xNoArr[i]);
                            long yVal = Long.parseLong(yNoArr[i]);
                            if (xVal < yVal) {
                                return -1;
                            }
                            if (xVal > yVal) {
                                return 1;
                            }
                        }
                        return Integer.compare(xNoArr.length, yNoArr.length);
                    }
            );
        }
        
        /**
         * 耗时信息
         */
        public static String prettyResult(@Nonnull List<TaskInfo> endTaskList) {
            StringBuilder sb = new StringBuilder();
            int size = endTaskList.size();
            if (size == 0) {
                sb.append("no tasks time info.");
                return sb.toString();
            }
            if (size == 1) {
                TaskInfo taskInfo = endTaskList.get(0);
                long consumeMilli = (taskInfo.getEndTime() - taskInfo.getStartTime()) / 1000000;
                return consumeMilli + "ms @ " + taskInfo.getTaskName();
            }
            Manager.sortByTaskNo(endTaskList);
            for (TaskInfo taskInfo : endTaskList) {
                String no = taskInfo.getTaskNo();
                String[] noArr = no.split("\\.");
                sb.append("\n");
                sb.append(StringUtils.rightPad("|--".repeat(Math.max(0, noArr.length - 1)) + no, 4, "."));
                long consumeMilli = (taskInfo.getEndTime() - taskInfo.getStartTime()) / 1000000;
                sb.append(StringUtils.leftPad(consumeMilli + "", 6, "."))
                        .append("ms")
                        .append(" @ ")
                        .append(taskInfo.getTaskName());
            }
            return sb.toString();
        }
        
        /**
         * 耗时信息
         */
        public static String result(@Nonnull List<TaskInfo> endTaskList) {
            int size = endTaskList.size();
            if (size == 0) {
                return "no tasks time info.";
            }
            if (size == 1) {
                TaskInfo taskInfo = endTaskList.get(0);
                long consumeMilli = (taskInfo.getEndTime() - taskInfo.getStartTime()) / 1000000;
                return consumeMilli + "ms @ " + taskInfo.getTaskName();
            }
            Manager.sortByTaskNo(endTaskList);
            List<String> list = new ArrayList<>();
            for (TaskInfo taskInfo : endTaskList) {
                StringBuilder sb = new StringBuilder();
                String no = taskInfo.getTaskNo();
                sb.append("taskNo: ");
                sb.append(no);
                sb.append(", ");
                long consumeMilli = (taskInfo.getEndTime() - taskInfo.getStartTime()) / 1000000;
                sb.append("consumeTime: ");
                sb.append(consumeMilli)
                        .append("ms, ")
                        .append("taskName: ")
                        .append(taskInfo.getTaskName());
                list.add(sb.toString());
            }
            return JSON.toJSONString(list);
        }
    }
    
    /**
     * 开始统计
     * <pre>
     * 若计时器尚未初始化,则会静默忽略当前操作
     * </pre>
     *
     * @param taskName 任务名
     */
    public static void start(String taskName) {
        if (!initThreadLocal.get()) {
            return;
        }
        TaskInfo taskInfo = new TaskInfo();
        String no = Manager.nextNo();
        taskInfo.setTaskNo(no);
        taskInfo.setTaskName(taskName);
        taskInfo.setStartTime(System.nanoTime());
        runningTaskThreadLocal.get().addFirst(taskInfo);
        maxTaskNoThreadLocal.set(no);
    }
    
    /**
     * 结束统计
     * <pre>
     * 若计时器尚未初始化,则会静默忽略当前操作
     * </pre>
     */
    public static void stop() {
        if (!initThreadLocal.get()) {
            return;
        }
        LinkedList<TaskInfo> runningTaskDeque = runningTaskThreadLocal.get();
        if (runningTaskDeque.size() == 0) {
            return;
        }
        TaskInfo taskInfo = runningTaskDeque.pollFirst();
        taskInfo.setEndTime(System.nanoTime());
        endTaskThreadLocal.get().addLast(taskInfo);
    }
    
    /**
     * 执行
     *
     * @param taskName 任务名
     * @param function 业务逻辑块
     * @param param 参数
     *
     * @return 逻辑执行结果
     */
    public static <P, R> R watch(@Nonnull String taskName, Function<P, R> function, P param) {
        try {
            start(taskName);
            return function.apply(param);
        } finally {
            stop();
        }
    }
    
    /**
     * 执行
     *
     * @param taskName 任务名
     * @param function 业务逻辑块
     *
     * @return 执行结果
     */
    public static <R> R watch(@Nonnull String taskName, InnerNoArgFunction<R> function) {
        try {
            start(taskName);
            return function.apply();
        } finally {
            stop();
        }
    }
    
    /**
     * 执行
     *
     * @param taskName 任务名
     * @param consumer 业务逻辑块
     * @param param 参数
     */
    public static <P> void voidWatch(@Nonnull String taskName, Consumer<P> consumer, P param) {
        try {
            start(taskName);
            consumer.accept(param);
        } finally {
            stop();
        }
    }
    
    /**
     * 执行
     *
     * @param taskName 任务名
     * @param consumer 业务逻辑块
     */
    public static void voidWatch(@Nonnull String taskName, InnerNoArgConsumer consumer) {
        try {
            start(taskName);
            consumer.accept();
        } finally {
            stop();
        }
    }
    
    /**
     * 耗时信息
     */
    public static String prettyResult() {
        if (!initThreadLocal.get()) {
            throw new IllegalStateException("Not initialized yet. You could do it by 'TimeWatchUtil.Manager.init()', "
                    + "and don't forget to clear him last by 'TimeWatchUtil.Manager.clear()'");
        }
        if (!Manager.currIsEnd()) {
            throw new IllegalStateException("There are also running tasks. " + Manager.getIngTaskList());
        }
        List<TaskInfo> endTaskList = Manager.getEndTaskList();
        return Manager.prettyResult(endTaskList);
    }
    
    /**
     * 耗时信息
     */
    public static String result() {
        if (!initThreadLocal.get()) {
            throw new IllegalStateException("Not initialized yet. You could do it by 'TimeWatchUtil.Manager.init()', "
                    + "and don't forget to clear him last by 'TimeWatchUtil.Manager.clear()'");
        }
        if (!Manager.currIsEnd()) {
            throw new IllegalStateException("There are also running tasks. " + Manager.getIngTaskList());
        }
        List<TaskInfo> endTaskList = Manager.getEndTaskList();
        return Manager.result(endTaskList);
    }
    
    /**
     * 任务信息
     */
    public static class TaskInfo {
        
        /** 执行序号(格式形如:xxx.xxx.xxx) */
        private String taskNo;
        
        /** 任务名 */
        private String taskName;
        
        /** 开始时间(纳秒) */
        private long startTime;
        
        /** 结束时间(纳秒) */
        private long endTime;
    
        public String getTaskNo() {
            return taskNo;
        }
    
        public void setTaskNo(String taskNo) {
            this.taskNo = taskNo;
        }
    
        public String getTaskName() {
            return taskName;
        }
    
        public void setTaskName(String taskName) {
            this.taskName = taskName;
        }
    
        public long getStartTime() {
            return startTime;
        }
    
        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }
    
        public long getEndTime() {
            return endTime;
        }
    
        public void setEndTime(long endTime) {
            this.endTime = endTime;
        }
    
        @Override
        public String toString() {
            return "TaskInfo{" +
                    "no='" + taskNo + '\'' +
                    ", taskName='" + taskName + '\'' +
                    ", startTime=" + startTime +
                    ", endTime=" + endTime +
                    '}';
        }
    }
    
    /**
     * (non-javadoc)
     */
    @FunctionalInterface
    public interface InnerNoArgFunction<R> {
        R apply();
    }
    
    /**
     * (non-javadoc)
     */
    @FunctionalInterface
    public interface InnerNoArgConsumer {
        void accept();
    }
    
}

第二步:配置总开关过滤器(示例)
@Component
public class TimeWatcherDemoFilter implements Filter {
    
    private final boolean prettyResult = true;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        HttpServletRequest httpServletRequest = null;
        if (request instanceof HttpServletRequest) {
            httpServletRequest = (HttpServletRequest)request;
        }
        try {
            WatchTimeUtil.Manager.init();
            String requestUri = "";
            if (httpServletRequest != null) {
                requestUri = httpServletRequest.getRequestURI();
            }
            WatchTimeUtil.start("request-uri -> " + requestUri);
            chain.doFilter(request, response);
        } finally {
            try {
                WatchTimeUtil.stop();
                if (log.isInfoEnabled()) {
                      log.info("consume time -> {}", prettyResult ? WatchTimeUtil.prettyResult() : WatchTimeUtil.result());
                 }
            } catch (Exception e) {
                log.warn("WatchTimeUtil.stop occur exception. e.getMessage() -> {}", e.getMessage());
            }
            WatchTimeUtil.Manager.clear();
        }
    }
}
第三步(可选):自定义aop注解
// 这里仅给出切面类核心逻辑demo
 try {
     String taskName;
     if (useDefaultAnnoValue || StringUtils.isBlank(annotation.value())) {
         taskName = clazzSimpleName + "#" + methodName;
     } else {
         taskName = annotation.value();
     }
     WatchTimeUtil.start(taskName);
     obj = methodInvocation.proceed();
 } finally {
     WatchTimeUtil.stop();
 }
第四步:使用耗时器统计器
  • 使用方式一:通过注解(假设你自定义的注解名为@WatchTime
     @WatchTime(taskName = "查询用户拥有的角色")
     public Collection<Long> queryRole(Long userId) {
         ...
     }
    
  • 使用方式二:使用工具类(示例)
    // 示例一
    try {
        WatchTimeUtil.start("查询用户基本信息");
        mockLogic();
    } finally {
        WatchTimeUtil.stop();
    }
    
    // 示例二
    WatchTimeUtil.voidWatch("查询用户安全信息", () -> mockLogic()); // 方法无返回值
    //Object returnVal = WatchTimeUtil.watch("查询用户安全信息", () -> mockLogic()); // 方法有返回值
    

相关资料

  • 基于获取方式一的示例代码(timewatcher/demo分支)

http://www.kler.cn/a/385500.html

相关文章:

  • 智慧仓储物流可视化平台
  • 03WIFI与蓝牙1——基于全志V3S的Linux开发板教程笔记
  • 【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
  • Docker使用docker-compose一键部署nacos、Mysql、redis
  • PyTorch:torchvision中的dataset的使用
  • sql专题 之 count()区别
  • 深度学习-图像评分实验(TensorFlow框架运用、读取处理图片、模型建构)
  • 【数据结构】快慢指针探秘:理解链表与数组中的环结构
  • Leecode热题100-78.子集
  • 【AIGC探索】AI实现PPT生产全流程
  • 《Python使用sqlite3数据库》
  • Pytorch基本语法
  • 微软域名邮箱:如何设置管理烽火域名邮箱?
  • .NET6中WPF项目添加System.Windows.Forms引用
  • oracle数据坏块处理(三)-数据抽取插入到新表中
  • webWorker基本用法
  • 容器化技术入门:Docker详解
  • 微服务相关问题
  • Redis - Zset 有序集合
  • 停止的 Docker 容器占用的内存和其他资源
  • python3的基本数据类型: 元组的其他操作
  • 华宇TAS应用中间件入围鲲鹏应用创新大赛2024全国总决赛
  • 案例精选 | 河北省某检察院安全运营中异构日志数据融合的实践探索
  • FreeSWITCH 验证
  • 【主机游戏】艾尔登法环游戏攻略
  • 关于Dell r730xd 老服务器的阵列卡 配置系统盘RAID 1