线程级耗时统计工具类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, 34, "."));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;}@Overridepublic String toString() {return "TaskInfo{" +"no='" + taskNo + '\'' +", taskName='" + taskName + '\'' +", startTime=" + startTime +", endTime=" + endTime +'}';}}/*** (non-javadoc)*/@FunctionalInterfacepublic interface InnerNoArgFunction<R> {R apply();}/*** (non-javadoc)*/@FunctionalInterfacepublic interface InnerNoArgConsumer {void accept();}}
第二步:配置总开关过滤器(示例)
@Component
public class TimeWatcherDemoFilter implements Filter {private final boolean prettyResult = true;@Overridepublic 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注解
// 这里仅给出切面类核心逻辑demotry {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.rhkb.cn/news/467273.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arkUI:Flex弹性布局的各个属性

arkUI&#xff1a;Flex弹性布局的简单使用 1 主要内容说明2 相关内容2.1 Flex弹性布局的方向2.1.1 源码1的简答说明2.1.2 源码1 &#xff08;Flex弹性布局的方向&#xff09;2.1.3 源码1运行效果2.1.3.1 当direction: FlexDirection.RowReverse2.1.3.2 当direction: FlexDirect…

详解Gemini API的使用:在国内实现大模型对话与目标检测教程

摘要&#xff1a;本博客介绍了如何利用Gemini API实现多轮对话和图像目标检测识别功能&#xff0c;在Python中快速搭建自己的大模型完成实际任务。通过详细的步骤解析&#xff0c;介绍了如何申请Gemini API密钥&#xff0c;调用API、对话实现的代码&#xff0c;给出了上传图片识…

5G时代已来:我们该如何迎接超高速网络?

内容概要 随着5G技术的普及&#xff0c;我们的生活似乎变得更加“科幻”了。想象一下&#xff0c;未来的智能家居将不仅仅是能够听你说“开灯”&#xff1b;它们可能会主动询问你今天心情如何&#xff0c;甚至会推荐你一杯“维他命C芒果榨汁”&#xff0c;帮助你抵御夏天的炎热…

算法每日练 -- 双指针篇(持续更新中)

介绍&#xff1a; 常见的双指针有两种形式&#xff0c;一种是对撞指针&#xff08;左右指针&#xff09;&#xff0c;一种是快慢指针&#xff08;前后指针&#xff09;。需要注意这里的双指针不是 int* 之类的类型指针&#xff0c;而是使用数组下标模拟地址来进行遍历的方式。 …

理解鸿蒙app 开发中的 context

是什么 Context是应用中对象的上下文&#xff0c;其提供了应用的一些基础信息&#xff0c;例如resourceManager&#xff08;资源管理&#xff09;、applicationInfo&#xff08;当前应用信息&#xff09;、dir&#xff08;应用文件路径&#xff09;、area&#xff08;文件分区…

贝尔不等式,路径积分与AB(Aharonov-Bohm)效应

贝尔不等式、路径积分与Aharonov-Bohm&#xff08;AB&#xff09;效应 这些概念分别源于量子力学不同的理论分支和思想实验&#xff0c;但它们都揭示了量子力学的奇异性质&#xff0c;包括非局域性、相位效应和波粒二象性。以下详细解析每一概念&#xff0c;并探讨其相互联系。…

python 爬虫 入门 六、Selenium

Selenium本来是一个自动测试工具&#xff0c;用于模拟用户对网站进行操作。在爬虫领域也有其用处。 一、下载安装Selenium及附属插件 pip install Selenium 安装完成后还需要安装一个浏览器驱动&#xff0c;来让python能启动浏览器。 如果是Edge或者其他基于Chromium的浏览器…

Linux(CentOS)yum update -y 事故

CentOS版本&#xff1a;CentOS 7 事情经过&#xff1a; 1、安装好CentOS 7&#xff0c;系统自带JDK8&#xff0c;版本为&#xff1a;1.8.0_181 2、安装好JDK17&#xff0c;版本为&#xff1a;17.0.13 3、为了安装MySQL执行了 yum update -y&#xff08;这个时候不知道该命令的…

uniapp uni-calendar日历实现考勤统计功能

根据日历组件代码结构 构成相应结构的状态统计数据 list 再遍历到每日的子组件中 <view class"uni-calendar__weeks-item" v-for"(weeks,weeksIndex) in item" :key"weeksIndex"><calendar-item class"uni-calendar-item--hook&q…

环境配置与搭建

安装pytorch 官网连链接&#xff1a;https://pytorch.org/ 特殊包名 cv2 pip install opencv-python sklearn pip install scikit-learnPIL pip install Pillow使用jupyter notebook pip install jupyter安装显卡驱动 Windows Linux 视频教程&#xff1a; 【ubuntu2…

【数据库实验一】数据库及数据库中表的建立实验

目录 实验1 学习RDBMS的使用和创建数据库 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六、实验结果 七、评价分析及心得体会 实验2 定义表和数据库完整性 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六…

SpringBoot健身房管理:技术与实践

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

CulturalBench :一个旨在评估大型语言模型在全球不同文化背景下知识掌握情况的基准测试数据集

2024-10-04&#xff0c;为了提升大型语言模型在不同文化背景下的实用性&#xff0c;华盛顿大学、艾伦人工智能研究所等机构联合创建了CulturalBench。这个数据集包含1,227个由人类编写和验证的问题&#xff0c;覆盖了包括被边缘化地区在内的45个全球区域。CulturalBench的推出&…

python登录功能实现

一.用python实现基本的登录功能 #-----------------1.基本登录功能------------------- nameinput("qq账号&#xff1a;") if name"jc":passwdinput("密码&#xff1a;")if passwd"123456":print("登录成功")else:print(&q…

如何使用Python管理环境变量

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 环境变量 📒📝 环境变量简介📝 Python 中的环境变量操作📝 获取环境变量📝 设置环境变量🔖 临时设置🔖 永久设置📝 删除环境变量📝 临时删除📝 永久删除📝 小结⚓️ 相关链接 ⚓️📖 介绍 📖 环境变量…

设置允许多用户远程登录 Windows 云服务器

操作场景 本文档以 Windows Server 2016 操作系统云服务器为例&#xff0c;指导您配置多用户远程登录 Windows 云服务器。 注意&#xff1a; 微软提供的多用户远程登录功能试用期为120天&#xff0c;若未购买多用户登录授权&#xff08;RDS CALs&#xff09;&#xff0c;则试…

喜报!景联文科技成功通过DCMM数据管理能力成熟度二级认证

10月30日&#xff0c;中国电子信息行业联合会公示了新一批DCMM贯标企业&#xff0c;景联文科技成功通过DCMM数据管理能力成熟度二级认证&#xff08;乙方认证&#xff09;。 DCMM是《数据管理能力成熟度评估模型》的简称&#xff0c;是我国在数据管理领域首个正式发布的国家标准…

Android setContentView执行流程(1)-生成DecorView

setContentView的流程主要就是讲在Activity的onCreate方法中调用setContentView方法之后&#xff0c;我们自定义的xml文件加载的过程&#xff0c;学习它可以让我们对整个View树的理解更加透彻&#xff0c;并且通过源码的学习&#xff0c;我们可以从根本上理解一些问题&#xff…

《操作系统 - 清华大学》2 -1:操作系统的启动

文章目录 0. 内容摘要1. 计算机体系机构概述2.启动2.1 启动时计算机内存和磁盘布局2.2. 内存映射 3. 系统调用、异常、中断3.1 定义3.2 背景3.3 中断、异常和系统调用的不同点3.3.1 源头3.3.2 处理时间3.3.3 响应 0. 内容摘要 两部分的内容 第一部分是启动。知道操作系统怎么是…

在服务器里安装2个conda

1、安装新的conda 下载地址&#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 本文选择&#xff1a;Anaconda3-2023.03-1-Linux-x86_64.sh 安装&#xff1a;Ubuntu安装Anaconda详细步骤&#xff08;Ubuntu22.04.1&#xff…