基于 Slf4j 和 AOP 的主动化方法实行时间日志记录方案

打印 上一主题 下一主题

主题 808|帖子 808|积分 2424

前言

实在这个需求很简朴,但是这个需求又是项目中必不可少的,尤其对于性能调优这块,但是使用哪种方式更加方便呢,这就是本篇博文需要讨论的重点
体系时间

可以通过 System.currentTimeMillis() 或 System.nanoTime() 来实现。
以下是一个简朴的示例,展示如何记录某段代码的实行时间并打印日志:
  1. import java.util.logging.Logger;
  2. public class TimeLogger {
  3.     private static final Logger logger = Logger.getLogger(TimeLogger.class.getName());
  4.     public static void main(String[] args) {
  5.         long startTime = System.nanoTime(); // 记录开始时间
  6.         // 你的代码逻辑
  7.         performTask();
  8.         long endTime = System.nanoTime(); // 记录结束时间
  9.         long duration = endTime - startTime; // 计算耗时
  10.         logger.info("Task executed in " + duration + " nanoseconds.");
  11.     }
  12.     private static void performTask() {
  13.         try {
  14.             Thread.sleep(2000); // 模拟耗时操作
  15.         } catch (InterruptedException e) {
  16.             e.printStackTrace();
  17.         }
  18.     }
  19. }
复制代码
解释:


  • System.nanoTime():返回当前时间的纳秒值,适合计算时间差。
  • 计算耗时后,通过日志框架(如 Logger)打印出来。
如果你使用的是其他日志框架(如 Log4j 或 SLF4J),可以相应地调整日志打印部分。
这个方法实用于丈量较精确的时间差。如果是需要秒级别的时间,可以使用 System.currentTimeMillis()。
使用 Instant 和 Duration(Java 8+)

Java 8 引入了 java.time 包,可以使用 Instant 来获取时间戳,并使用 Duration 来计算时间差。
  1. import java.time.Duration;
  2. import java.time.Instant;
  3. import java.util.logging.Logger;
  4. public class TimeLogger {
  5.     private static final Logger logger = Logger.getLogger(TimeLogger.class.getName());
  6.     public static void main(String[] args) {
  7.         Instant start = Instant.now(); // 记录开始时间
  8.         // 你的代码逻辑
  9.         performTask();
  10.         Instant end = Instant.now(); // 记录结束时间
  11.         Duration duration = Duration.between(start, end); // 计算时间差
  12.         logger.info("Task executed in " + duration.toMillis() + " milliseconds.");
  13.     }
  14.     private static void performTask() {
  15.         try {
  16.             Thread.sleep(2000); // 模拟耗时操作
  17.         } catch (InterruptedException e) {
  18.             e.printStackTrace();
  19.         }
  20.     }
  21. }
复制代码
使用 StopWatch(Apache Commons Lang)

StopWatch 是 Apache Commons Lang 提供的一个便捷工具类,用于丈量时间。它简朴易用,并且可以多次启动和停止。
起首,你需要引入 Apache Commons Lang 库:
  1. <dependency>
  2.     <groupId>org.apache.commons</groupId>
  3.     <artifactId>commons-lang3</artifactId>
  4.     <version>3.12.0</version>
  5. </dependency>
复制代码
然后,可以像如许使用 StopWatch:
  1. import org.apache.commons.lang3.time.StopWatch;
  2. import java.util.logging.Logger;
  3. public class TimeLogger {
  4.     private static final Logger logger = Logger.getLogger(TimeLogger.class.getName());
  5.     public static void main(String[] args) {
  6.         StopWatch stopWatch = new StopWatch();
  7.         stopWatch.start(); // 开始计时
  8.         // 你的代码逻辑
  9.         performTask();
  10.         stopWatch.stop(); // 停止计时
  11.         logger.info("Task executed in " + stopWatch.getTime() + " milliseconds.");
  12.     }
  13.     private static void performTask() {
  14.         try {
  15.             Thread.sleep(2000); // 模拟耗时操作
  16.         } catch (InterruptedException e) {
  17.             e.printStackTrace();
  18.         }
  19.     }
  20. }
复制代码
使用 AOP 进行日志记录(面向切面编程)

如果你希望在多个方法或类中都记录耗时,可以使用 Spring AOP 或其他 AOP 框架来主动化这一过程。如许,你就不需要在每个方法中显式地编写时间计算代码。
假设你使用的是 Spring 框架,可以通过 AOP 实现:
自界说一个注解

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. public @interface PrintExecutionTime {
  4. }
复制代码
面向切面

  1. @Slf4j
  2. @Aspect
  3. @Component
  4. @RequiredArgsConstructor
  5. public class ExecutionTimeAspect {
  6.     private final LogConfig logConfig;
  7.     @Around("@annotation(PrintExecutionTime)")
  8.     public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
  9.         long startTime = System.currentTimeMillis();
  10.         Object result = joinPoint.proceed();
  11.         long endTime = System.currentTimeMillis();
  12.         long executionTime = endTime - startTime;
  13.         if (executionTime > logConfig.getTime()) {
  14.             log.warn("Code method time-consuming, class: {}, executionTime: {} ms", joinPoint.getSignature().toString(),
  15.                 executionTime);
  16.         }
  17.         return result;
  18.     }
  19. }
复制代码
使用 Slf4j + Logback 配合计时器(比方,通过 MDC 传递信息)

如果你已经在使用 SLF4J 和 Logback,SLF4J 提供了一些扩展,可以通过 MDC 来传递方法的实行时间等信息。
  1. import org.slf4j.MDC;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. public class TimeLogger {
  5.     private static final Logger logger = LoggerFactory.getLogger(TimeLogger.class);
  6.     public static void main(String[] args) {
  7.         long startTime = System.nanoTime(); // 记录开始时间
  8.         // 你的代码逻辑
  9.         performTask();
  10.         long endTime = System.nanoTime(); // 记录结束时间
  11.         long duration = endTime - startTime; // 计算耗时
  12.         MDC.put("executionTime", String.valueOf(duration)); // 将耗时信息放入 MDC
  13.         logger.info("Task executed.");
  14.         MDC.clear(); // 清理 MDC
  15.     }
  16.     private static void performTask() {
  17.         try {
  18.             Thread.sleep(2000); // 模拟耗时操作
  19.         } catch (InterruptedException e) {
  20.             e.printStackTrace();
  21.         }
  22.     }
  23. }
复制代码
在 Logback 设置中,你可以使用 %X{executionTime} 来输出 MDC 中的 executionTime:
  1. <layout class="ch.qos.logback.classic.pattern.PatternLayout">
  2.     <Pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg - Execution Time: %X{executionTime}ms%n</Pattern>
  3. </layout>
复制代码
Slf4j + Logback +MDC+AOP

在实际项目中使用 Slf4j + Logback 配合计时器 并通过 MDC (Mapped Diagnostic Context) 传递信息来记录方法实行时间是一个很常见且高效的方案。如允许以在日志输出中直接查看方法的实行时间,而无需在每个方法中显式记录时间。
  1. import org.aspectj.lang.annotation.Aspect;
  2. import org.aspectj.lang.annotation.Around;
  3. import org.aspectj.lang.annotation.Pointcut;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.slf4j.MDC;
  6. import org.springframework.stereotype.Component;
  7. import java.time.Duration;
  8. import java.time.Instant;
  9. @Aspect
  10. @Component
  11. public class PerformanceAspect {
  12.     @Pointcut("execution(* com.example..*(..))") // 定义切点,匹配指定包下的所有方法
  13.     public void performanceLogging() {
  14.     }
  15.     @Around("performanceLogging()") // 环绕通知
  16.     public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
  17.         // 记录方法开始时间
  18.         Instant start = Instant.now();
  19.         // 执行目标方法
  20.         Object proceed = joinPoint.proceed();
  21.         // 记录方法结束时间
  22.         Instant end = Instant.now();
  23.         long executionTime = Duration.between(start, end).toMillis();
  24.         // 将耗时信息传入 MDC
  25.         MDC.put("executionTime", String.valueOf(executionTime));
  26.         // 打印日志
  27.         String methodName = joinPoint.getSignature().toShortString();
  28.         // 如果你在日志中想要包括方法名称、类名等,可以在这里添加
  29.         logger.info("Method {} executed in {} ms", methodName, executionTime);
  30.         // 清除 MDC,避免 MDC 数据影响其他日志
  31.         MDC.clear();
  32.         return proceed;
  33.     }
  34. }
复制代码
  1. <configuration>
  2.     <!-- Define the pattern for logging -->
  3.     <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  4.         <encoder>
  5.             <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg - Execution Time: %X{executionTime}ms%n</pattern>
  6.         </encoder>
  7.     </appender>
  8.     <!-- Root logger definition -->
  9.     <root level="debug">
  10.         <appender-ref ref="console"/>
  11.     </root>
  12. </configuration>
复制代码
使用 @EnableAspectJAutoProxy
如果你使用 Spring Boot,可以通过 @EnableAspectJAutoProxy 启用 AOP
在 @SpringBootApplication 或设置类中添加:
  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  4. @SpringBootApplication
  5. @EnableAspectJAutoProxy
  6. public class Application {
  7.     public static void main(String[] args) {
  8.         SpringApplication.run(Application.class, args);
  9.     }
  10. }
复制代码
总结

  1. [开始方法] → [AOP 拦截] → [开始计时] → [业务逻辑执行] → [结束计时] → [MDC 存储时间]
  2.                                ↓
  3.                              [日志输出] ← [MDC 输出时间]  
复制代码
通过团结 Slf4j + Logback 和 AOP,我们可以主动化地记录方法实行的时间,并将其通过 MDC 传递到日志体系中,而无需手动添加时间记录的代码。这种方式在生产环境中非常方便,可以让你实时相识应用的性能瓶颈,且对代码的侵入性非常低。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

千千梦丶琪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表