基于 Slf4j 和 AOP 的自动化方法执行时间日志记录方案
前言
其实这个需求很简单,但是这个需求又是项目中必不可少的,尤其对于性能调优这块,但是使用哪种方式更加方便呢,这就是本篇博文需要讨论的重点
系统时间
可以通过 System.currentTimeMillis() 或 System.nanoTime() 来实现。
以下是一个简单的示例,展示如何记录某段代码的执行时间并打印日志:
import java.util.logging.Logger;public class TimeLogger {private static final Logger logger = Logger.getLogger(TimeLogger.class.getName());public static void main(String[] args) {long startTime = System.nanoTime(); // 记录开始时间// 你的代码逻辑performTask();long endTime = System.nanoTime(); // 记录结束时间long duration = endTime - startTime; // 计算耗时logger.info("Task executed in " + duration + " nanoseconds.");}private static void performTask() {try {Thread.sleep(2000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}}
}
解释:
- System.nanoTime():返回当前时间的纳秒值,适合计算时间差。
- 计算耗时后,通过日志框架(如 Logger)打印出来。
如果你使用的是其他日志框架(如 Log4j 或 SLF4J),可以相应地调整日志打印部分。
这个方法适用于测量较精确的时间差。如果是需要秒级别的时间,可以使用 System.currentTimeMillis()。
使用 Instant 和 Duration(Java 8+)
Java 8 引入了 java.time 包,可以使用 Instant 来获取时间戳,并使用 Duration 来计算时间差。
import java.time.Duration;
import java.time.Instant;
import java.util.logging.Logger;public class TimeLogger {private static final Logger logger = Logger.getLogger(TimeLogger.class.getName());public static void main(String[] args) {Instant start = Instant.now(); // 记录开始时间// 你的代码逻辑performTask();Instant end = Instant.now(); // 记录结束时间Duration duration = Duration.between(start, end); // 计算时间差logger.info("Task executed in " + duration.toMillis() + " milliseconds.");}private static void performTask() {try {Thread.sleep(2000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}}
}
使用 StopWatch(Apache Commons Lang)
StopWatch 是 Apache Commons Lang 提供的一个便捷工具类,用于测量时间。它简单易用,并且可以多次启动和停止。
首先,你需要引入 Apache Commons Lang 库:
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version>
</dependency>
然后,可以像这样使用 StopWatch:
import org.apache.commons.lang3.time.StopWatch;
import java.util.logging.Logger;public class TimeLogger {private static final Logger logger = Logger.getLogger(TimeLogger.class.getName());public static void main(String[] args) {StopWatch stopWatch = new StopWatch();stopWatch.start(); // 开始计时// 你的代码逻辑performTask();stopWatch.stop(); // 停止计时logger.info("Task executed in " + stopWatch.getTime() + " milliseconds.");}private static void performTask() {try {Thread.sleep(2000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}}
}
使用 AOP 进行日志记录(面向切面编程)
如果你希望在多个方法或类中都记录耗时,可以使用 Spring AOP 或其他 AOP 框架来自动化这一过程。这样,你就不需要在每个方法中显式地编写时间计算代码。
假设你使用的是 Spring 框架,可以通过 AOP 实现:
自定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PrintExecutionTime {}
面向切面
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class ExecutionTimeAspect {private final LogConfig logConfig;@Around("@annotation(PrintExecutionTime)")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed();long endTime = System.currentTimeMillis();long executionTime = endTime - startTime;if (executionTime > logConfig.getTime()) {log.warn("Code method time-consuming, class: {}, executionTime: {} ms", joinPoint.getSignature().toString(),executionTime);}return result;}
}
使用 Slf4j + Logback 配合计时器(例如,通过 MDC 传递信息)
如果你已经在使用 SLF4J 和 Logback,SLF4J 提供了一些扩展,可以通过 MDC 来传递方法的执行时间等信息。
import org.slf4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class TimeLogger {private static final Logger logger = LoggerFactory.getLogger(TimeLogger.class);public static void main(String[] args) {long startTime = System.nanoTime(); // 记录开始时间// 你的代码逻辑performTask();long endTime = System.nanoTime(); // 记录结束时间long duration = endTime - startTime; // 计算耗时MDC.put("executionTime", String.valueOf(duration)); // 将耗时信息放入 MDClogger.info("Task executed.");MDC.clear(); // 清理 MDC}private static void performTask() {try {Thread.sleep(2000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}}
}
在 Logback 配置中,你可以使用 %X{executionTime} 来输出 MDC 中的 executionTime:
<layout class="ch.qos.logback.classic.pattern.PatternLayout"><Pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg - Execution Time: %X{executionTime}ms%n</Pattern>
</layout>
Slf4j + Logback +MDC+AOP
在实际项目中使用 Slf4j + Logback 配合计时器 并通过 MDC (Mapped Diagnostic Context) 传递信息来记录方法执行时间是一个很常见且高效的方案。这样可以在日志输出中直接查看方法的执行时间,而无需在每个方法中显式记录时间。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;import java.time.Duration;
import java.time.Instant;@Aspect
@Component
public class PerformanceAspect {@Pointcut("execution(* com.example..*(..))") // 定义切点,匹配指定包下的所有方法public void performanceLogging() {}@Around("performanceLogging()") // 环绕通知public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {// 记录方法开始时间Instant start = Instant.now();// 执行目标方法Object proceed = joinPoint.proceed();// 记录方法结束时间Instant end = Instant.now();long executionTime = Duration.between(start, end).toMillis();// 将耗时信息传入 MDCMDC.put("executionTime", String.valueOf(executionTime));// 打印日志String methodName = joinPoint.getSignature().toShortString();// 如果你在日志中想要包括方法名称、类名等,可以在这里添加logger.info("Method {} executed in {} ms", methodName, executionTime);// 清除 MDC,避免 MDC 数据影响其他日志MDC.clear();return proceed;}
}
<configuration><!-- Define the pattern for logging --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg - Execution Time: %X{executionTime}ms%n</pattern></encoder></appender><!-- Root logger definition --><root level="debug"><appender-ref ref="console"/></root></configuration>
使用 @EnableAspectJAutoProxy
如果你使用 Spring Boot,可以通过 @EnableAspectJAutoProxy 启用 AOP
在 @SpringBootApplication 或配置类中添加:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
总结
[开始方法] → [AOP 拦截] → [开始计时] → [业务逻辑执行] → [结束计时] → [MDC 存储时间]↓[日志输出] ← [MDC 输出时间]
通过结合 Slf4j + Logback 和 AOP,我们可以自动化地记录方法执行的时间,并将其通过 MDC 传递到日志系统中,而无需手动添加时间记录的代码。这种方式在生产环境中非常方便,可以让你实时了解应用的性能瓶颈,且对代码的侵入性非常低。
相关文章:
基于 Slf4j 和 AOP 的自动化方法执行时间日志记录方案
前言 其实这个需求很简单,但是这个需求又是项目中必不可少的,尤其对于性能调优这块,但是使用哪种方式更加方便呢,这就是本篇博文需要讨论的重点 系统时间 可以通过 System.currentTimeMillis() 或 System.nanoTime() 来实现。 …...
关于 K8s 的一些基础概念整理-补充【k8s系列之二】
〇、前言 本文继续整理下 K8s 的一些基础概念,作为前一篇概念汇总的补充。 前一篇博文链接:关于 K8s 的一些基础概念整理【k8s系列之一】_集群 master节点 控制节点 宿主机-CSDN博客 一、详情 1.1 Label Label 在 k8s 中是一个非常核心的概念…...
FPGA的DMA应用——pcileech
硬件通过pcie总线,访存本机的内存,并进行修改,可以进行很多操作。 学习视频:乱讲DMA及TLP 1-pcileech项目简介和自定义模块介绍_哔哩哔哩_bilibili vivado2024.1的下载文章链接和地址:AMD-Xilinx Vivado™ 2024.1 现…...
信息安全管理:运行管理checklist
运行管理checklist内容包括日常操作与维护管理、变更管理、备份与故障恢复、应急与业务连续性管理等内容。 一、日常操作与维护管理 ▼▼制度与流程 是否建立日常运行操作制度与流程?包括网络、主机、应用等方面的操作制度与流程? 是否明确相关部门和人…...
Linux系统之stat命令的基本使用
Linux系统之stat命令的基本使用 一、stat命令 介绍二、stat命令帮助2.1 查询帮助信息2.2 stat命令的帮助解释 三、stat命令的基本使用3.1 查询文件信息3.2 查看文件系统状态3.3 使用格式化输出3.4 以简洁形式打印信息 四、注意事项 一、stat命令 介绍 stat 命令用于显示文件或文…...
云手机+Facebook:让科技与娱乐完美结合
移动互联网时代,Facebook作为全球最大的社交媒体平台之一,早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现,则为Facebook营销注入了新的活力,其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…...
为什么要在PHY芯片和RJ45网口中间加网络变压器
在PHY芯片和RJ45网口之间加入网络变压器是出于以下几个重要的考虑: 1. 电气隔离:网络变压器提供了电气隔离功能,有效阻断了PHY芯片与RJ45之间直流分量的直接连接。这样可以防止可能的电源冲突,降低系统故障的风险,并保…...
LeetCode 19:删除链表的倒数第N 个结点
题目: 地址:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 方法一: 方法二: 代码: package com.zy.leetcode.LeetCode_19;/*** Author: zy* Date: 2024-12-25-13:01* Description: 删除链表…...
RT-DETR融合[IJCV2024]LSKNet中的LSKBlock模块
RT-DETR使用教程: RT-DETR使用教程 RT-DETR改进汇总贴:RT-DETR更新汇总贴 《Large Selective Kernel Network for Remote Sensing Object Detection》 一、 模块介绍 论文链接:https://arxiv.org/pdf/2303.09030 代码链接:https:…...
C/C++ 数据结构与算法【树和森林】 树和森林 详细解析【日常学习,考研必备】带图+详细代码
一、树的存储结构 1)双亲表示法实现: 定义结构数组存放树的结点,每个结点含两个域: 数据域:存放结点本身信息。双亲域:指示本结点的双亲结点在数组中的位置。 特点:找双亲简单,找孩子难 C语…...
新浪微博大数据面试题及参考答案(数据开发和数据分析)
介绍一下你所掌握的计算机网络和操作系统相关知识 计算机网络:计算机网络是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。我掌握了网络协议…...
OpenHarmony怎么修改DPI密度值?RK3566鸿蒙开发板演示
本文介绍在开源鸿蒙OpenHarmony系统下,修改DPI密度值的方法,触觉智能Purple Pi OH鸿蒙开发板演示,搭载了瑞芯微RK3566四核处理器,Laval鸿蒙社区推荐开发板,已适配全新开源鸿蒙OpenHarmony5.0 Release系统,适…...
SAP GUI Scripting - 如何判断组件是否存在
总体来说,SAP Scripting 与 BDC 类似,因为是屏幕录制,就可能碰到不同的情况,比如每个录入的数据不同,可能出现一个对话框,或者出现一个状态栏消息。这种任何有变化的情况,在 Scripting 中没有考…...
Go 计算Utf8字符串的长度 不要超过mysql字段的最大长度
背景: 我有一个mysql的字段,是utf8格式的,但有时候前端传的字符串会超长,为此我需要在后端接口,先判断是否超长,如果超长,则报错提示前端。 代码: // 计算utf8下,字符串…...
llamafactory报错:双卡4090GPU,训练qwen2.5:7B、14B时报错GPU显存不足(out of memory),轻松搞定~~~
实际问题场景: 使用llamafactory进行微调qwen2.5 7B和14B的大模型时,会出现out of memory的报错。尝试使用降低batch_size(原本是2,现在降到1)的方式,可以让qwen2.5:7B跑起来,但时不时会不稳定…...
全局webSocket 单个页面进行监听并移除单页面监听
之前全局封装的 webSocket 在某些特定的页面中使用会直接去调用 webSocket 的 onMessage 方法 已进入页面就会调,如果退出页面移除整个监听的话全局监听就会被移除 这是修改后的 全局封装 let token uni.getStorageSync(token) const HEARTBEAT_INTERVAL 1 *…...
JVM调优实践篇
理论篇 1多功能养鱼塘-JVM内存 大鱼塘O(可分配内存): JVM可以调度使用的总的内存数,这个数量受操作系统进程寻址范围、系统虚拟内存总数、系统物理内存总数、其他系统运行所占用的内存资源等因素的制约。 小池塘A&a…...
【JavaEE】Spring Web MVC
目录 一、Spring Web MVC简介 1.1 MVC简介1.2 Spring MVC1.3 RequestMapping注解1.3.1 使用1.3.2 RequestMapping的请求设置 1.3.2.1 方法11.3.2.2 方法2 二、Postman介绍 2.1 创建请求2.2 界面如下:2.3 传参介绍 一、Spring Web MVC简介 官方文档介绍ÿ…...
VSCode 插件开发实战(七):插件支持了哪些事件,以及如何利用和监听这些事件
前言 VSCode 作为现代开发者的首选编辑器之一,其核心优势在于其高度可扩展性。通过自定义插件,开发者可以根据自己的需求对编辑器进行功能扩展和优化。在这些插件开发过程中,事件处理和监听机制尤为重要,它们允许插件在特定事件发…...
指针详解之 多层嵌套的关系
1 例子之指向3个字符串的指针数组,易混淆! 1.1过程详解: char *str[3]{ "Hello,thisisasample!", "Hi,goodmorning.", "Helloworld" }; char s[80]; strcpy(s,str[0]); //也可写成strcpy(s,*st…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
