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

基于 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 的自动化方法执行时间日志记录方案

前言 其实这个需求很简单&#xff0c;但是这个需求又是项目中必不可少的&#xff0c;尤其对于性能调优这块&#xff0c;但是使用哪种方式更加方便呢&#xff0c;这就是本篇博文需要讨论的重点 系统时间 可以通过 System.currentTimeMillis() 或 System.nanoTime() 来实现。 …...

关于 K8s 的一些基础概念整理-补充【k8s系列之二】

〇、前言 本文继续整理下 K8s 的一些基础概念&#xff0c;作为前一篇概念汇总的补充。 前一篇博文链接&#xff1a;关于 K8s 的一些基础概念整理【k8s系列之一】_集群 master节点 控制节点 宿主机-CSDN博客 一、详情 1.1 Label Label 在 k8s 中是一个非常核心的概念&#xf…...

FPGA的DMA应用——pcileech

硬件通过pcie总线&#xff0c;访存本机的内存&#xff0c;并进行修改&#xff0c;可以进行很多操作。 学习视频&#xff1a;乱讲DMA及TLP 1-pcileech项目简介和自定义模块介绍_哔哩哔哩_bilibili vivado2024.1的下载文章链接和地址&#xff1a;AMD-Xilinx Vivado™ 2024.1 现…...

信息安全管理:运行管理checklist

运行管理checklist内容包括日常操作与维护管理、变更管理、备份与故障恢复、应急与业务连续性管理等内容。 一、日常操作与维护管理 ▼▼制度与流程 是否建立日常运行操作制度与流程&#xff1f;包括网络、主机、应用等方面的操作制度与流程&#xff1f; 是否明确相关部门和人…...

Linux系统之stat命令的基本使用

Linux系统之stat命令的基本使用 一、stat命令 介绍二、stat命令帮助2.1 查询帮助信息2.2 stat命令的帮助解释 三、stat命令的基本使用3.1 查询文件信息3.2 查看文件系统状态3.3 使用格式化输出3.4 以简洁形式打印信息 四、注意事项 一、stat命令 介绍 stat 命令用于显示文件或文…...

云手机+Facebook:让科技与娱乐完美结合

移动互联网时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现&#xff0c;则为Facebook营销注入了新的活力&#xff0c;其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…...

为什么要在PHY芯片和RJ45网口中间加网络变压器

在PHY芯片和RJ45网口之间加入网络变压器是出于以下几个重要的考虑&#xff1a; 1. 电气隔离&#xff1a;网络变压器提供了电气隔离功能&#xff0c;有效阻断了PHY芯片与RJ45之间直流分量的直接连接。这样可以防止可能的电源冲突&#xff0c;降低系统故障的风险&#xff0c;并保…...

LeetCode 19:删除链表的倒数第N 个结点

题目&#xff1a; 地址&#xff1a;https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 方法一&#xff1a; 方法二&#xff1a; 代码&#xff1a; package com.zy.leetcode.LeetCode_19;/*** Author: zy* Date: 2024-12-25-13:01* Description: 删除链表…...

RT-DETR融合[IJCV2024]LSKNet中的LSKBlock模块

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《Large Selective Kernel Network for Remote Sensing Object Detection》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/pdf/2303.09030 代码链接&#xff1a;https:…...

C/C++ 数据结构与算法【树和森林】 树和森林 详细解析【日常学习,考研必备】带图+详细代码

一、树的存储结构 1&#xff09;双亲表示法实现&#xff1a; 定义结构数组存放树的结点&#xff0c;每个结点含两个域: 数据域&#xff1a;存放结点本身信息。双亲域&#xff1a;指示本结点的双亲结点在数组中的位置。 特点&#xff1a;找双亲简单&#xff0c;找孩子难 C语…...

新浪微博大数据面试题及参考答案(数据开发和数据分析)

介绍一下你所掌握的计算机网络和操作系统相关知识 计算机网络:计算机网络是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。我掌握了网络协议…...

OpenHarmony怎么修改DPI密度值?RK3566鸿蒙开发板演示

本文介绍在开源鸿蒙OpenHarmony系统下&#xff0c;修改DPI密度值的方法&#xff0c;触觉智能Purple Pi OH鸿蒙开发板演示&#xff0c;搭载了瑞芯微RK3566四核处理器&#xff0c;Laval鸿蒙社区推荐开发板&#xff0c;已适配全新开源鸿蒙OpenHarmony5.0 Release系统&#xff0c;适…...

SAP GUI Scripting - 如何判断组件是否存在

总体来说&#xff0c;SAP Scripting 与 BDC 类似&#xff0c;因为是屏幕录制&#xff0c;就可能碰到不同的情况&#xff0c;比如每个录入的数据不同&#xff0c;可能出现一个对话框&#xff0c;或者出现一个状态栏消息。这种任何有变化的情况&#xff0c;在 Scripting 中没有考…...

Go 计算Utf8字符串的长度 不要超过mysql字段的最大长度

背景&#xff1a; 我有一个mysql的字段&#xff0c;是utf8格式的&#xff0c;但有时候前端传的字符串会超长&#xff0c;为此我需要在后端接口&#xff0c;先判断是否超长&#xff0c;如果超长&#xff0c;则报错提示前端。 代码&#xff1a; // 计算utf8下&#xff0c;字符串…...

llamafactory报错:双卡4090GPU,训练qwen2.5:7B、14B时报错GPU显存不足(out of memory),轻松搞定~~~

实际问题场景&#xff1a; 使用llamafactory进行微调qwen2.5 7B和14B的大模型时&#xff0c;会出现out of memory的报错。尝试使用降低batch_size&#xff08;原本是2&#xff0c;现在降到1&#xff09;的方式&#xff0c;可以让qwen2.5:7B跑起来&#xff0c;但时不时会不稳定…...

全局webSocket 单个页面进行监听并移除单页面监听

之前全局封装的 webSocket 在某些特定的页面中使用会直接去调用 webSocket 的 onMessage 方法 已进入页面就会调&#xff0c;如果退出页面移除整个监听的话全局监听就会被移除 这是修改后的 全局封装 let token uni.getStorageSync(token) const HEARTBEAT_INTERVAL 1 *…...

JVM调优实践篇

理论篇 1多功能养鱼塘&#xff0d;JVM内存 大鱼塘O&#xff08;可分配内存&#xff09;&#xff1a; JVM可以调度使用的总的内存数&#xff0c;这个数量受操作系统进程寻址范围、系统虚拟内存总数、系统物理内存总数、其他系统运行所占用的内存资源等因素的制约。 小池塘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 界面如下&#xff1a;2.3 传参介绍 一、Spring Web MVC简介 官方文档介绍&#xff…...

VSCode 插件开发实战(七):插件支持了哪些事件,以及如何利用和监听这些事件

前言 VSCode 作为现代开发者的首选编辑器之一&#xff0c;其核心优势在于其高度可扩展性。通过自定义插件&#xff0c;开发者可以根据自己的需求对编辑器进行功能扩展和优化。在这些插件开发过程中&#xff0c;事件处理和监听机制尤为重要&#xff0c;它们允许插件在特定事件发…...

指针详解之 多层嵌套的关系

1 例子之指向3个字符串的指针数组&#xff0c;易混淆&#xff01; 1.1过程详解&#xff1a; char *str[3]{ "Hello,thisisasample!", "Hi,goodmorning.", "Helloworld" }; char s[80]&#xff1b; strcpy(s,str[0]); //也可写成strcpy(s,*st…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...