一个注解解决接口耗时日志的打印

在日常开发中,常常需要统计方法的耗时情况,一般的写法是在进入方法之前,记录一下当前时间戳,在方法最后再用当前时间戳减去进入时候的时间戳就是耗时情况,方法很简单,但不够优雅。
接下来我们用一个注解+AOP的方式来实现这个需求

首先,自定义一个注解 @StopWatch

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 方法耗时日志
 *
 * @author 敖癸
 * @formatter:on
 * @since 2023/11/29
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StopWatch {

    /** 日志内容模板, ex: "topic: {}" */
    String value() default "";

    /** 模板参数,支持SpEL表达式, ex: {"#serialNo", "第二个参数"} */
    String[] args() default {};
}

然后再实现一个切面


import cn.hutool.core.lang.Opt;
import com.dmjy.pub.common.StopWatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.MDC;

/**
 * 耗时日志注解切面
 *
 * @author 敖癸
 * @formatter:on
 * @since 2023/11/29
 */
@Slf4j
@Aspect
@RequiredArgsConstructor
public class StopWatchAspect {

    private static final SpelExpressionParser PARSER = new SpelExpressionParser();
    
    @Around("@annotation(stopWatch)")
    public Object around(ProceedingJoinPoint joinPoint, StopWatch stopWatch) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } finally {
            long totalMs = System.currentTimeMillis() - startTime;
            MDC.put("costTime", String.valueOf(totalMs));
            String content = buildTemplate(joinPoint, stopWatch);
            content = Opt.ofBlankAble(content).orElseGet(() -> joinPoint.getSignature().toShortString());
            if (log.isDebugEnabled()) {
                log.debug("{}, 执行耗时 {} ms", content, totalMs);
            } else {
                log.info("{}, 执行耗时 {} ms", content, totalMs);
            }
        }
    }


    /**
     * SpEL模板解析
     *
     * @param context     切点的上下文对象
     * @param template    字符串模板
     * @param expressions SpEL参数
     * @return java.lang.String
     * @author 敖癸
     * @since 2024/3/5 - 17:03
     */
    private static String buildTemplate(ProceedingJoinPoint joinPoint, StopWatch stopWatch) {
        String template = stopWatch.value();
        String[] expressions = stopWatch.args();
        if (StrUtil.isNotBlank(template) && expressions != null) {
            EvaluationContext context = buildEvaluationContext(joinPoint)
            Object[] args = Arrays.stream(expressions).map(expression -> {
                if (StrUtil.startWith(expression, "#")) {
                    return PARSER.parseExpression(expression).getValue(context);
                }
                return expression;
            }).toArray();
            template = StrUtil.format(template, args);
        }
        return template;
    }
    
    /**
     * 解析切点的上下文对象
     *
     * @param joinPoint aop切点
     * @return org.springframework.expression.EvaluationContext
     * @author 敖癸
     * @since 2024/3/5 - 17:05
     */
    private static EvaluationContext buildEvaluationContext(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] argNames = signature.getParameterNames();
        EvaluationContext context = new StandardEvaluationContext();

        Object[] methodArgs = joinPoint.getArgs();
        for (int i = 0; i < methodArgs.length; i++) {
            context.setVariable(argNames[i], methodArgs[i]);
        }
        return context;
    }
}

使用示例:

    @StopWatch(value = "whatsappUser回调: phone: {}, {}", args = {"#dto.vcPhone", "#dto.isMatch()?\"匹配成功\":\"匹配失败\""})
    public void consumerFunc(WSUserInfoDTO dto) {
        ThreadUtil.sleep(345);
    }

在这里插入图片描述
这个注解不光可以取出参数中对象中的属性,方法,还可以解析表达式,来自定义日志的内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/466675.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

吴恩达机器学习笔记 二十四 决策树模型 学习过程 什么时候停止分裂 如何选择结点特征

案例&#xff1a;识别小猫&#xff0c;上面这个分类的特征 x 采用分类值&#xff08;几个离散的值&#xff09; 决策树最顶端的结点称根结点(root node)&#xff0c;除了根结点和叶子结点之外的叫决策结点(decision node)&#xff0c;最底层的叫叶子结点(leaf node)&#xff0c…

PHP反序列化--_wakeup()绕过

一、漏洞原理&#xff1a; 二、靶场复现: 进入靶场&#xff0c;分析源代码&#xff1a; <?php error_reporting(0); class secret{var $fileindex.php;public function __construct($file){$this->file$file;}function __destruct(){include_once($this->file);ech…

2024年3月GESP认证Scratch图形化编程四级真题及答案

GESP 图形化四级试卷 &#xff08;满分&#xff1a;100 分 考试时间&#xff1a;120 分钟&#xff09; 学校&#xff1a; 姓名&#xff1a; ​ 一、单选题&#xff08;共 10 题&#xff0c;每题 2 分&#xff0c;共 30 分&#xff09; 题号 1 2 3 4 5 6 7 8 9 10 11 1…

外包干了5天,技术退步明显。。。。

说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&a…

十九、软考-系统架构设计师笔记-真题解析-2021年真题

软考-系统架构设计师-2021年上午选择题真题 考试时间 8:30 ~ 11:00 150分钟 1.前趋图(Precedence Graph)是一个有向无环图&#xff0c;记为&#xff1a;→(Pi,Pj)Pi must Complete Before Pj may strat), 假设系统中进程P{P1, P2,P3,P4, P5, P6, P7, P8}&#xff0c; 且进程的…

Python 常用的开源爬虫库介绍

Python 是一种广泛使用的编程语言&#xff0c;特别是在 Web 爬虫领域。有许多优秀的开源爬虫库可以帮助开发者高效地抓取网页内容。以下是几个常用的 Python 爬虫库及其特点和优势&#xff1a; BeautifulSoup 特点 - **HTML/XML 解析**&#xff1a;BeautifulSoup 是一个…

Sora 惊艳亮相,会对哪些大学专业带来影响?

近日&#xff0c;OpenAI 发布了「文生视频」工具 Sora&#xff0c;AI 技术变革又一次震撼了整个世界。根据 OpenAI 的介绍&#xff0c;通过「一次性为模型提供多帧的预测」&#xff0c;Sora 可以生成长达一分钟的视频&#xff0c;并能实现单视频的多角度镜头切换&#xff0c;还…

C语言每日一题(64)快乐数

题目链接 力扣网202 快乐数 题目描述 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不…

SAP Business Application Studio(BAS) 中Git的使用

1. 概要 本文将介绍如何在SAP BAS中使用Git。 2. BAS中Git功能的集成方式 2.1 简化版Git视图&#xff08;Simplified Git View&#xff09; 通过简化版Git视图&#xff0c;开发人员可以执行最常用的一些Git操作&#xff0c;例如&#xff1a; 初始化或克隆一个仓库reposito…

python中isinstance函数判断各种类型的小细节

1. 基本语法 isinstance(object, classinfo) Return true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof. Also return true if classinfo is a type object (new-style class) and object is…

【送书福利!第一期】《ARM汇编与逆向工程》

&#x1f42e;博主syst1m 带你 acquire knowledge&#xff01; ✨博客首页——syst1m的博客&#x1f498; &#x1f618;《CTF专栏》超级详细的解析&#xff0c;宝宝级教学让你从蹒跚学步到健步如飞&#x1f648; &#x1f60e;《大数据专栏》大数据从0到秃头&#x1f47d;&…

语音信号数字编码总共有哪些

语音信号的数字编码主要用于将模拟语音信号转换为数字形式&#xff0c;以便可以通过数字网络传输&#xff0c;或者存储在数字存储媒介上。存在多种语音编码标准&#xff0c;各自有不同的编码方式、比特率和应用场景。以下是目前广泛使用的语音编码标准&#xff1a; 1. G.711&…

消息队列面试题

目录 1. 为什么使用消息队列 2. 消息队列的缺点 3. 消息队列如何选型&#xff1f; 4. 如何保证消息队列是高可用的 5. 如何保证消息不被重复消费&#xff08;见第二条&#xff09; 6. 如何保证消息的可靠性传输&#xff1f; 7. 如何保证消息的顺序性&#xff08;即消息幂…

革新监测技术:无线数据记录系统如何颠覆食品、医疗和制药行业的验证流程

在过去的 10-15 年中&#xff0c;无线数据记录系统逐渐取代了热电偶系统&#xff0c;用于食品、医疗和制药行业的验证。过去&#xff0c;使用记录仪的一个主要缺点是在研究过程中缺乏实时数据&#xff0c;但由于 虹科EllabSKY 选项可以提供来自无线设备的实时数据&#xff0c;这…

C语言自定义库

编写 xx.c 和xx.h文件\将源代码编译为目标文件 gcc -c add.c sub.c 执行完毕后会生产add.o和sub.o文件静态库创建使用ar命令&#xff1b; ar -r libmymath.a add.o sub.o将库和main.c文件一起编译 gcc -o main main.c -lmymath -L./ 注意 上述书写格式不要错乱 -L 是指定文件路…

排序算法:快速排序(递归)

文章目录 一、创始人托尼霍尔的快速排序二、挖坑法三、前后指针法 所属专栏:C初阶 引言&#xff1a;这里所说的快速排序有三种&#xff0c;第一种是霍尔大佬自创的&#xff0c;还有一种叫做挖坑法&#xff0c;另外一种叫前后指针法 一、创始人托尼霍尔的快速排序 1.这里我们先…

AI跟踪报道第33期-新加坡内哥谈技术-AI新闻快报:GTC和终结GPU/TPU的热力学未来Chip?

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【gpt实践】比OpenAI 的 GPT-4 更好模型 Claude 3.0

Google 最近发布了最新的 Gemini 1.5 语言模型&#xff0c;震惊了世界。这是目前功能最强大的模型&#xff0c;拥有 100 万个上下文窗口&#xff0c;是所有大型基础模型中最大的。 OpenAI 的 GPT-4 才具有 128K 上下文窗口。 最近&#xff0c;谷歌最接近的竞争对手之一 Anthro…

【算法】AC自动机的优化:增量更新与删除

一、概述 AC自动机&#xff08;Aho-Corasick Automation&#xff09;是著名的多模匹配算法&#xff0c;源于贝尔实验室&#xff0c;并且在实际应用中得到广泛的引用&#xff0c;且具有以下特点&#xff1a; 只需要扫描一次文本&#xff0c;即可获取所有匹配该文本的模式串复杂…

svg代码应用于button

将svg代码的path属性应用于按钮内容&#xff0c;去掉按钮边框&#xff0c;并且自适应svg大小&#xff0c;以下实现的是一个旋转按钮。 svg代码如下(iconfont下载)&#xff1a; <svg t"1710741485848" class"icon" viewBox"0 0 1024 1024" ve…