1. 基于AOP的耗时监控

1.1 添加依赖

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

1.2 自定义监控注解

java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeMonitor {
    String value() default "";
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
    boolean logResult() default false;
}

1.3 实现AOP切面

java

@Aspect
@Component
@Slf4j
public class ExecutionTimeAspect {
    
    @Around("@annotation(timeMonitor)")
    public Object monitorExecutionTime(ProceedingJoinPoint joinPoint, 
                                     TimeMonitor timeMonitor) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = joinPoint.proceed();
            return result;
        } finally {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            long convertedDuration = convertTimeUnit(duration, timeMonitor.timeUnit());
            
            logExecutionTime(joinPoint, timeMonitor, convertedDuration, result);
        }
    }
    
    private long convertTimeUnit(long duration, TimeUnit timeUnit) {
        switch (timeUnit) {
            case SECONDS: return duration / 1000;
            case MICROSECONDS: return duration * 1000;
            case NANOSECONDS: return duration * 1000000;
            default: return duration;
        }
    }
    
    private void logExecutionTime(ProceedingJoinPoint joinPoint, TimeMonitor timeMonitor, 
                                long duration, Object result) {
        String methodName = joinPoint.getSignature().toShortString();
        String message = String.format("方法 %s 执行耗时: %d %s", 
            methodName, duration, getTimeUnitString(timeMonitor.timeUnit()));
        
        if (timeMonitor.logResult() && result != null) {
            message += String.format(" | 返回结果: %s", result.toString());
        }
        
        log.info(message);
    }
    
    private String getTimeUnitString(TimeUnit timeUnit) {
        switch (timeUnit) {
            case SECONDS: return "s";
            case MILLISECONDS: return "ms";
            case MICROSECONDS: return "μs";
            case NANOSECONDS: return "ns";
            default: return "ms";
        }
    }
}

1.4 使用示例

java

@Service
public class UserService {
    
    @TimeMonitor(value = "用户查询", timeUnit = TimeUnit.MILLISECONDS, logResult = true)
    public User getUserById(Long id) {
        // 业务逻辑
        return userRepository.findById(id).orElse(null);
    }
    
    @TimeMonitor("批量用户查询")
    public List<User> getUsers(List<Long> ids) {
        // 业务逻辑
        return userRepository.findAllById(ids);
    }
}

2. 基于Spring Boot Actuator的监控

2.1 添加依赖

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
</dependency>

2.2 配置Metrics监控

java

@Component
public class MethodMetrics {
    
    private final MeterRegistry meterRegistry;
    private final Map<String, Timer> timers = new ConcurrentHashMap<>();
    
    public MethodMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    public void recordExecutionTime(String methodName, long duration, TimeUnit unit) {
        Timer timer = timers.computeIfAbsent(methodName, 
            key -> Timer.builder("method.execution.time")
                       .tag("method", methodName)
                       .register(meterRegistry));
        
        timer.record(duration, unit);
    }
    
    @EventListener
    public void handleMethodExecutionEvent(MethodExecutionEvent event) {
        recordExecutionTime(event.getMethodName(), 
                          event.getDuration(), 
                          event.getTimeUnit());
    }
}

3. 高级功能:统计和告警

3.1 监控统计类

java

@Component
@Slf4j
public class MethodPerformanceMonitor {
    
    private final Map<String, MethodStats> statsMap = new ConcurrentHashMap<>();
    private final long warningThreshold;
    
    public MethodPerformanceMonitor(@Value("${monitor.warning-threshold:1000}") long warningThreshold) {
        this.warningThreshold = warningThreshold;
    }
    
    public void recordMethodExecution(String methodName, long duration) {
        MethodStats stats = statsMap.computeIfAbsent(methodName, k -> new MethodStats());
        stats.recordExecution(duration);
        
        // 超过阈值告警
        if (duration > warningThreshold) {
            log.warn("方法 {} 执行耗时 {}ms 超过阈值 {}ms", 
                    methodName, duration, warningThreshold);
        }
        
        // 定期输出统计信息
        if (stats.getExecutionCount() % 100 == 0) {
            log.info("方法 {} 统计: {}", methodName, stats.getStatsSummary());
        }
    }
    
    @Scheduled(fixedRate = 60000) // 每分钟输出一次汇总统计
    public void printSummary() {
        log.info("=== 方法执行耗时统计汇总 ===");
        statsMap.forEach((method, stats) -> {
            log.info("方法 {}: {}", method, stats.getStatsSummary());
        });
    }
    
    @Data
    public static class MethodStats {
        private long executionCount;
        private long totalTime;
        private long maxTime;
        private long minTime = Long.MAX_VALUE;
        
        public void recordExecution(long duration) {
            executionCount++;
            totalTime += duration;
            maxTime = Math.max(maxTime, duration);
            minTime = Math.min(minTime, duration);
        }
        
        public double getAverageTime() {
            return executionCount == 0 ? 0 : (double) totalTime / executionCount;
        }
        
        public String getStatsSummary() {
            return String.format("调用次数: %d, 平均耗时: %.2fms, 最大耗时: %dms, 最小耗时: %dms", 
                    executionCount, getAverageTime(), maxTime, minTime);
        }
    }
}

3.2 增强的AOP切面

java

@Aspect
@Component
@Slf4j
public class EnhancedExecutionTimeAspect {
    
    private final MethodPerformanceMonitor performanceMonitor;
    
    public EnhancedExecutionTimeAspect(MethodPerformanceMonitor performanceMonitor) {
        this.performanceMonitor = performanceMonitor;
    }
    
    @Around("@annotation(timeMonitor)")
    public Object monitorExecutionTime(ProceedingJoinPoint joinPoint, 
                                     TimeMonitor timeMonitor) throws Throwable {
        String methodName = getMethodName(joinPoint);
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            return result;
        } catch (Throwable throwable) {
            log.error("方法 {} 执行异常", methodName, throwable);
            throw throwable;
        } finally {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            
            // 记录执行时间
            performanceMonitor.recordMethodExecution(methodName, duration);
            
            // 记录详细日志
            if (log.isDebugEnabled()) {
                log.debug("方法 {} 执行耗时: {}ms", methodName, duration);
            }
        }
    }
    
    private String getMethodName(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getDeclaringType().getSimpleName() + "." + signature.getName();
    }
}

4. 配置类

java

@Configuration
@EnableAspectJAutoProxy
@EnableScheduling
public class MonitorConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public MethodPerformanceMonitor methodPerformanceMonitor() {
        return new MethodPerformanceMonitor(1000L);
    }
    
    @Bean
    public EnhancedExecutionTimeAspect enhancedExecutionTimeAspect(
            MethodPerformanceMonitor performanceMonitor) {
        return new EnhancedExecutionTimeAspect(performanceMonitor);
    }
}

5. 应用配置

yaml

# application.yml
monitor:
  warning-threshold: 500  # 告警阈值,单位ms
  
logging:
  level:
    com.yourpackage.monitor: DEBUG

management:
  endpoints:
    web:
      exposure:
        include: metrics
  endpoint:
    metrics:
      enabled: true

使用方式

  1. 基本使用:在需要监控的方法上添加 @TimeMonitor 注解

  2. 自定义配置:通过注解参数调整时间单位和是否记录返回值

  3. 查看统计:系统会自动输出方法执行统计信息

  4. 监控告警:当方法执行时间超过阈值时会输出警告日志

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐