google-site-verification: google61fe8ba583a51912.html
USE Method for Saturation Analysis in Java

Introduction

The USE Method (Utilization, Saturation, Errors) is a systematic approach for analyzing system performance and identifying bottlenecks. It focuses on three key metrics for every resource: Utilization (percentage time busy), Saturation (queue length or wait time), and Errors (error counts).

Architecture Overview

USE Method Framework

public class USEMethodFramework { /** * USE Method Components: * 1. Resource Discovery - Identify system resources * 2. Metric Collection - Gather Utilization, Saturation, Errors * 3. Analysis Engine - Identify bottlenecks and saturation points * 4. Reporting - Generate actionable insights */ private ResourceDiscoverer resourceDiscoverer; private MetricCollector metricCollector; private AnalysisEngine analysisEngine; private ReportGenerator reportGenerator; public USEAnalysis analyzeSystem() { List<SystemResource> resources = resourceDiscoverer.discoverResources(); Map<SystemResource, USEMetrics> metrics = metricCollector.collectMetrics(resources); List<Bottleneck> bottlenecks = analysisEngine.analyze(metrics); return reportGenerator.generateReport(metrics, bottlenecks); } }

Core Implementation

Resource Model

public enum ResourceType { CPU, MEMORY, DISK_IO, NETWORK_IO, DATABASE_CONNECTION, THREAD_POOL, GARBAGE_COLLECTOR, EXTERNAL_SERVICE, MESSAGE_QUEUE, CACHE } public class SystemResource { private final String id; private final String name; private final ResourceType type; private final String description; private final Map<String, String> attributes; public SystemResource(String id, String name, ResourceType type, String description, Map<String, String> attributes) { this.id = id; this.name = name; this.type = type; this.description = description; this.attributes = attributes != null ? attributes : new HashMap<>(); } // Builder pattern public static class Builder { private String id; private String name; private ResourceType type; private String description; private Map<String, String> attributes = new HashMap<>(); public Builder id(String id) { this.id = id; return this; } public Builder name(String name) { this.name = name; return this; } public Builder type(ResourceType type) { this.type = type; return this; } public Builder description(String description) { this.description = description; return this; } public Builder attribute(String key, String value) { this.attributes.put(key, value); return this; } public SystemResource build() { return new SystemResource(id, name, type, description, attributes); } } // Getters public String getId() { return id; } public ResourceType getType() { return type; } } public class USEMetrics { private final SystemResource resource; private final double utilization; // 0.0 to 1.0 (percentage) private final double saturation; // queue length or wait time private final long errors; // error count private final long timestamp; private final Map<String, Object> additionalMetrics; public USEMetrics(SystemResource resource, double utilization, double saturation, long errors, long timestamp, Map<String, Object> additionalMetrics) { this.resource = resource; this.utilization = utilization; this.saturation = saturation; this.errors = errors; this.timestamp = timestamp; this.additionalMetrics = additionalMetrics != null ? additionalMetrics : new HashMap<>(); } // Getters public double getUtilization() { return utilization; } public double getSaturation() { return saturation; } public long getErrors() { return errors; } public boolean isUtilizationCritical() { return utilization > 0.8; // 80% threshold } public boolean isSaturationCritical() { return saturation > 10; // Queue length > 10 } public boolean hasErrors() { return errors > 0; } }

Resource Discovery

@Component public class ResourceDiscoverer { private static final Logger logger = LoggerFactory.getLogger(ResourceDiscoverer.class); public List<SystemResource> discoverResources() { List<SystemResource> resources = new ArrayList<>(); // CPU Resources resources.addAll(discoverCPUResources()); // Memory Resources resources.addAll(discoverMemoryResources()); // Disk I/O Resources resources.addAll(discoverDiskResources()); // Network Resources resources.addAll(discoverNetworkResources()); // Application Resources resources.addAll(discoverApplicationResources()); logger.info("Discovered {} system resources", resources.size()); return resources; } private List<SystemResource> discoverCPUResources() { List<SystemResource> cpuResources = new ArrayList<>(); int processorCount = Runtime.getRuntime().availableProcessors(); for (int i = 0; i < processorCount; i++) { cpuResources.add(new SystemResource.Builder() .id("cpu-" + i) .name("CPU Core " + i) .type(ResourceType.CPU) .description("CPU processor core") .attribute("core_id", String.valueOf(i)) .build()); } // Overall CPU cpuResources.add(new SystemResource.Builder() .id("cpu-total") .name("Total CPU") .type(ResourceType.CPU) .description("Total system CPU") .build()); return cpuResources; } private List<SystemResource> discoverMemoryResources() { return Arrays.asList( new SystemResource.Builder() .id("memory-heap") .name("Heap Memory") .type(ResourceType.MEMORY) .description("JVM Heap Memory") .build(), new SystemResource.Builder() .id("memory-nonheap") .name("Non-Heap Memory") .type(ResourceType.MEMORY) .description("JVM Non-Heap Memory") .build(), new SystemResource.Builder() .id("memory-system") .name("System Memory") .type(ResourceType.MEMORY) .description("Total System Memory") .build() ); } private List<SystemResource> discoverDiskResources() { List<SystemResource> diskResources = new ArrayList<>(); try { File[] roots = File.listRoots(); for (File root : roots) { diskResources.add(new SystemResource.Builder() .id("disk-" + root.getAbsolutePath().replace(File.separator, "")) .name("Disk " + root.getAbsolutePath()) .type(ResourceType.DISK_IO) .description("Disk storage") .attribute("path", root.getAbsolutePath()) .build()); } } catch (Exception e) { logger.warn("Failed to discover disk resources", e); } return diskResources; } private List<SystemResource> discoverNetworkResources() { List<SystemResource> networkResources = new ArrayList<>(); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface ni = interfaces.nextElement(); if (ni.isUp() && !ni.isLoopback()) { networkResources.add(new SystemResource.Builder() .id("network-" + ni.getName()) .name("Network " + ni.getDisplayName()) .type(ResourceType.NETWORK_IO) .description("Network interface") .attribute("interface_name", ni.getName()) .build()); } } } catch (Exception e) { logger.warn("Failed to discover network resources", e); } return networkResources; } private List<SystemResource> discoverApplicationResources() { List<SystemResource> appResources = new ArrayList<>(); // Database connections appResources.add(new SystemResource.Builder() .id("db-connections") .name("Database Connections") .type(ResourceType.DATABASE_CONNECTION) .description("Database connection pool") .build()); // Thread pools appResources.add(new SystemResource.Builder() .id("thread-pool-main") .name("Main Thread Pool") .type(ResourceType.THREAD_POOL) .description("Main application thread pool") .build()); // Garbage collector appResources.add(new SystemResource.Builder() .id("gc-heap") .name("Garbage Collector") .type(ResourceType.GARBAGE_COLLECTOR) .description("JVM Garbage Collector") .build()); // External services appResources.add(new SystemResource.Builder() .id("external-api") .name("External API") .type(ResourceType.EXTERNAL_SERVICE) .description("External REST API service") .build()); return appResources; } }

Metric Collection

Comprehensive Metric Collector

@Component public class MetricCollector { private static final Logger logger = LoggerFactory.getLogger(MetricCollector.class); private final OperatingSystemMXBean osBean; private final RuntimeMXBean runtimeBean; private final ThreadMXBean threadBean; private final MemoryMXBean memoryBean; private final List<GarbageCollectorMXBean> gcBeans; private final MeterRegistry meterRegistry; public MetricCollector(MeterRegistry meterRegistry) { this.osBean = ManagementFactory.getOperatingSystemMXBean(); this.runtimeBean = ManagementFactory.getRuntimeMXBean(); this.threadBean = ManagementFactory.getThreadMXBean(); this.memoryBean = ManagementFactory.getMemoryMXBean(); this.gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); this.meterRegistry = meterRegistry; } public Map<SystemResource, USEMetrics> collectMetrics(List<SystemResource> resources) { Map<SystemResource, USEMetrics> metrics = new ConcurrentHashMap<>(); resources.parallelStream().forEach(resource -> { try { USEMetrics resourceMetrics = collectResourceMetrics(resource); metrics.put(resource, resourceMetrics); } catch (Exception e) { logger.warn("Failed to collect metrics for resource: {}", resource.getId(), e); } }); return metrics; } private USEMetrics collectResourceMetrics(SystemResource resource) { long timestamp = System.currentTimeMillis(); Map<String, Object> additionalMetrics = new HashMap<>(); switch (resource.getType()) { case CPU: return collectCPUMetrics(resource, timestamp, additionalMetrics); case MEMORY: return collectMemoryMetrics(resource, timestamp, additionalMetrics); case DISK_IO: return collectDiskMetrics(resource, timestamp, additionalMetrics); case NETWORK_IO: return collectNetworkMetrics(resource, timestamp, additionalMetrics); case THREAD_POOL: return collectThreadPoolMetrics(resource, timestamp, additionalMetrics); case GARBAGE_COLLECTOR: return collectGCMetrics(resource, timestamp, additionalMetrics); case DATABASE_CONNECTION: return collectDatabaseMetrics(resource, timestamp, additionalMetrics); case EXTERNAL_SERVICE: return collectExternalServiceMetrics(resource, timestamp, additionalMetrics); default: return new USEMetrics(resource, 0.0, 0.0, 0, timestamp, additionalMetrics); } } private USEMetrics collectCPUMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { double utilization = 0.0; double saturation = 0.0; long errors = 0; if (resource.getId().equals("cpu-total")) { // System CPU utilization if (osBean instanceof com.sun.management.OperatingSystemMXBean) { com.sun.management.OperatingSystemMXBean sunOsBean = (com.sun.management.OperatingSystemMXBean) osBean; utilization = sunOsBean.getSystemCpuLoad(); if (utilization < 0) utilization = 0.0; } // System load average as saturation metric double loadAverage = osBean.getSystemLoadAverage(); if (loadAverage > 0) { saturation = loadAverage; } additionalMetrics.put("available_processors", osBean.getAvailableProcessors()); additionalMetrics.put("system_load_average", osBean.getSystemLoadAverage()); } else { // Individual core metrics (simplified - Java doesn't provide per-core stats directly) utilization = 0.5; // Placeholder saturation = 0.0; } return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectMemoryMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { double utilization = 0.0; double saturation = 0.0; long errors = 0; MemoryUsage usage; if (resource.getId().equals("memory-heap")) { usage = memoryBean.getHeapMemoryUsage(); long max = usage.getMax(); long used = usage.getUsed(); utilization = max > 0 ? (double) used / max : 0.0; // Saturation: GC pressure saturation = calculateGCPressure(); } else if (resource.getId().equals("memory-nonheap")) { usage = memoryBean.getNonHeapMemoryUsage(); long max = usage.getMax(); long used = usage.getUsed(); utilization = max > 0 ? (double) used / max : 0.0; } else if (resource.getId().equals("memory-system")) { if (osBean instanceof com.sun.management.OperatingSystemMXBean) { com.sun.management.OperatingSystemMXBean sunOsBean = (com.sun.management.OperatingSystemMXBean) osBean; long totalMemory = sunOsBean.getTotalPhysicalMemorySize(); long freeMemory = sunOsBean.getFreePhysicalMemorySize(); long usedMemory = totalMemory - freeMemory; utilization = totalMemory > 0 ? (double) usedMemory / totalMemory : 0.0; additionalMetrics.put("total_memory_bytes", totalMemory); additionalMetrics.put("free_memory_bytes", freeMemory); additionalMetrics.put("used_memory_bytes", usedMemory); } } // Memory errors (OOMEs) errors = getMemoryErrorCount(); return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectDiskMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { double utilization = 0.0; double saturation = 0.0; long errors = 0; try { String path = resource.getAttributes().get("path"); if (path != null) { File file = new File(path); long totalSpace = file.getTotalSpace(); long freeSpace = file.getFreeSpace(); long usedSpace = totalSpace - freeSpace; utilization = totalSpace > 0 ? (double) usedSpace / totalSpace : 0.0; additionalMetrics.put("total_space_bytes", totalSpace); additionalMetrics.put("free_space_bytes", freeSpace); additionalMetrics.put("used_space_bytes", usedSpace); } } catch (Exception e) { logger.warn("Failed to collect disk metrics for {}", resource.getId(), e); errors++; } return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectNetworkMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { // Network metrics collection would require OS-specific implementation // or using libraries like OSHI or Sigar double utilization = 0.0; double saturation = 0.0; long errors = 0; return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectThreadPoolMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { double utilization = 0.0; double saturation = 0.0; long errors = 0; // Thread pool metrics int threadCount = threadBean.getThreadCount(); int peakThreadCount = threadBean.getPeakThreadCount(); int daemonThreadCount = threadBean.getDaemonThreadCount(); // Utilization: active threads vs peak utilization = peakThreadCount > 0 ? (double) threadCount / peakThreadCount : 0.0; // Saturation: thread wait time or queue length (simplified) saturation = calculateThreadSaturation(); additionalMetrics.put("thread_count", threadCount); additionalMetrics.put("peak_thread_count", peakThreadCount); additionalMetrics.put("daemon_thread_count", daemonThreadCount); return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectGCMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { double utilization = 0.0; double saturation = 0.0; long errors = 0; long totalGcTime = 0; long totalGcCount = 0; for (GarbageCollectorMXBean gcBean : gcBeans) { totalGcTime += gcBean.getCollectionTime(); totalGcCount += gcBean.getCollectionCount(); } // Utilization: GC time as percentage of uptime long uptime = runtimeBean.getUptime(); utilization = uptime > 0 ? (double) totalGcTime / uptime : 0.0; // Saturation: GC frequency saturation = totalGcCount; additionalMetrics.put("total_gc_time_ms", totalGcTime); additionalMetrics.put("total_gc_count", totalGcCount); return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectDatabaseMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { // Database connection pool metrics // This would integrate with HikariCP, Tomcat JDBC, etc. double utilization = 0.0; double saturation = 0.0; long errors = 0; try { // Example for HikariCP Object pool = getConnectionPool(); if (pool != null) { utilization = getPoolUtilization(pool); saturation = getPoolWaitCount(pool); errors = getPoolErrorCount(pool); } } catch (Exception e) { logger.warn("Failed to collect database metrics", e); } return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } private USEMetrics collectExternalServiceMetrics(SystemResource resource, long timestamp, Map<String, Object> additionalMetrics) { // External service metrics from Micrometer double utilization = 0.0; double saturation = 0.0; long errors = 0; try { Counter errorCounter = meterRegistry.find("http.server.requests").counter(); Timer requestTimer = meterRegistry.find("http.server.requests").timer(); if (errorCounter != null) { errors = (long) errorCounter.count(); } if (requestTimer != null) { // Use mean response time as saturation indicator saturation = requestTimer.mean(TimeUnit.MILLISECONDS); // Utilization based on request rate (simplified) utilization = Math.min(requestTimer.count() / 1000.0, 1.0); // Normalized } } catch (Exception e) { logger.warn("Failed to collect external service metrics", e); } return new USEMetrics(resource, utilization, saturation, errors, timestamp, additionalMetrics); } // Helper methods private double calculateGCPressure() { // Simplified GC pressure calculation long totalGcTime = gcBeans.stream() .mapToLong(GarbageCollectorMXBean::getCollectionTime) .sum(); long uptime = runtimeBean.getUptime(); return uptime > 0 ? (double) totalGcTime / uptime : 0.0; } private double calculateThreadSaturation() { // Thread saturation based on blocked threads long[] threadIds = threadBean.getAllThreadIds(); long blockedCount = Arrays.stream(threadIds) .filter(id -> threadBean.getThreadInfo(id) != null) .filter(id -> threadBean.getThreadInfo(id).getBlockedCount() > 0) .count(); return (double) blockedCount / threadIds.length; } private long getMemoryErrorCount() { // Count memory-related errors from logs or MBeans return 0; // Implementation would depend on error tracking system } // Placeholder methods for database pool metrics private Object getConnectionPool() { return null; } private double getPoolUtilization(Object pool) { return 0.0; } private double getPoolWaitCount(Object pool) { return 0.0; } private long getPoolErrorCount(Object pool) { return 0; } }

Analysis Engine

Bottleneck Detection

@Component public class AnalysisEngine { private static final Logger logger = LoggerFactory.getLogger(AnalysisEngine.class); private final double utilizationThreshold; private final double saturationThreshold; public AnalysisEngine() { this.utilizationThreshold = 0.8; // 80% this.saturationThreshold = 10.0; // Queue length 10 } public List<Bottleneck> analyze(Map<SystemResource, USEMetrics> metrics) { List<Bottleneck> bottlenecks = new ArrayList<>(); for (Map.Entry<SystemResource, USEMetrics> entry : metrics.entrySet()) { SystemResource resource = entry.getKey(); USEMetrics resourceMetrics = entry.getValue(); List<Bottleneck> resourceBottlenecks = analyzeResource(resource, resourceMetrics); bottlenecks.addAll(resourceBottlenecks); } // Sort by severity bottlenecks.sort(Comparator.comparing(Bottleneck::getSeverity).reversed()); logger.info("Found {} potential bottlenecks", bottlenecks.size()); return bottlenecks; } private List<Bottleneck> analyzeResource(SystemResource resource, USEMetrics metrics) { List<Bottleneck> bottlenecks = new ArrayList<>(); // Utilization analysis if (metrics.getUtilization() > utilizationThreshold) { bottlenecks.add(createUtilizationBottleneck(resource, metrics)); } // Saturation analysis if (metrics.getSaturation() > saturationThreshold) { bottlenecks.add(createSaturationBottleneck(resource, metrics)); } // Error analysis if (metrics.hasErrors()) { bottlenecks.add(createErrorBottleneck(resource, metrics)); } return bottlenecks; } private Bottleneck createUtilizationBottleneck(SystemResource resource, USEMetrics metrics) { double utilization = metrics.getUtilization(); Severity severity = calculateUtilizationSeverity(utilization); String description = String.format( "High utilization detected: %.1f%% (threshold: %.1f%%)", utilization * 100, utilizationThreshold * 100); String recommendation = generateUtilizationRecommendation(resource, utilization); return new Bottleneck( resource, BottleneckType.UTILIZATION, severity, description, recommendation, metrics.getTimestamp() ); } private Bottleneck createSaturationBottleneck(SystemResource resource, USEMetrics metrics) { double saturation = metrics.getSaturation(); Severity severity = calculateSaturationSeverity(saturation); String description = String.format( "High saturation detected: %.1f (threshold: %.1f)", saturation, saturationThreshold); String recommendation = generateSaturationRecommendation(resource, saturation); return new Bottleneck( resource, BottleneckType.SATURATION, severity, description, recommendation, metrics.getTimestamp() ); } private Bottleneck createErrorBottleneck(SystemResource resource, USEMetrics metrics) { long errors = metrics.getErrors(); Severity severity = calculateErrorSeverity(errors); String description = String.format("Errors detected: %d", errors); String recommendation = generateErrorRecommendation(resource, errors); return new Bottleneck( resource, BottleneckType.ERRORS, severity, description, recommendation, metrics.getTimestamp() ); } private Severity calculateUtilizationSeverity(double utilization) { if (utilization > 0.95) return Severity.CRITICAL; if (utilization > 0.90) return Severity.HIGH; if (utilization > 0.80) return Severity.MEDIUM; return Severity.LOW; } private Severity calculateSaturationSeverity(double saturation) { if (saturation > 100) return Severity.CRITICAL; if (saturation > 50) return Severity.HIGH; if (saturation > 10) return Severity.MEDIUM; return Severity.LOW; } private Severity calculateErrorSeverity(long errors) { if (errors > 100) return Severity.CRITICAL; if (errors > 10) return Severity.HIGH; if (errors > 0) return Severity.MEDIUM; return Severity.LOW; } private String generateUtilizationRecommendation(SystemResource resource, double utilization) { switch (resource.getType()) { case CPU: return "Consider optimizing CPU-intensive operations, scaling horizontally, or upgrading hardware"; case MEMORY: return "Investigate memory leaks, increase heap size, or optimize memory usage"; case DISK_IO: return "Optimize disk I/O operations, use faster storage, or implement caching"; case NETWORK_IO: return "Optimize network usage, increase bandwidth, or use compression"; case DATABASE_CONNECTION: return "Optimize database queries, increase connection pool size, or add connection pooling"; case THREAD_POOL: return "Increase thread pool size, optimize task execution, or use async processing"; default: return "Investigate resource usage and consider optimization or scaling"; } } private String generateSaturationRecommendation(SystemResource resource, double saturation) { switch (resource.getType()) { case CPU: return "High load average indicates processes waiting for CPU. Consider scaling or optimization"; case MEMORY: return "High GC pressure indicates memory contention. Investigate memory usage patterns"; case THREAD_POOL: return "Thread pool queue is growing. Increase pool size or optimize task processing"; case DATABASE_CONNECTION: return "Connection wait queue is growing. Increase pool size or optimize query performance"; default: return "Resource is experiencing high contention. Consider scaling or optimization"; } } private String generateErrorRecommendation(SystemResource resource, long errors) { switch (resource.getType()) { case MEMORY: return "Memory errors detected. Check for OutOfMemoryErrors and memory leaks"; case DATABASE_CONNECTION: return "Database connection errors. Check database health and connection settings"; case EXTERNAL_SERVICE: return "External service errors. Check service availability and implement retry logic"; default: return "Resource errors detected. Investigate error logs and implement error handling"; } } } public class Bottleneck { private final SystemResource resource; private final BottleneckType type; private final Severity severity; private final String description; private final String recommendation; private final long detectedAt; public Bottleneck(SystemResource resource, BottleneckType type, Severity severity, String description, String recommendation, long detectedAt) { this.resource = resource; this.type = type; this.severity = severity; this.description = description; this.recommendation = recommendation; this.detectedAt = detectedAt; } // Getters public Severity getSeverity() { return severity; } public BottleneckType getType() { return type; } public SystemResource getResource() { return resource; } } public enum BottleneckType { UTILIZATION, SATURATION, ERRORS } public enum Severity { CRITICAL, HIGH, MEDIUM, LOW }

Real-time Monitoring

Continuous USE Monitoring

@Component public class USEMonitor { private static final Logger logger = LoggerFactory.getLogger(USEMonitor.class); private final ResourceDiscoverer resourceDiscoverer; private final MetricCollector metricCollector; private final AnalysisEngine analysisEngine; private final ScheduledExecutorService scheduler; private final List<USEMetricsListener> listeners; private volatile boolean monitoring = false; private ScheduledFuture<?> monitoringTask; public USEMonitor(ResourceDiscoverer resourceDiscoverer, MetricCollector metricCollector, AnalysisEngine analysisEngine) { this.resourceDiscoverer = resourceDiscoverer; this.metricCollector = metricCollector; this.analysisEngine = analysisEngine; this.scheduler = Executors.newSingleThreadScheduledExecutor(); this.listeners = new CopyOnWriteArrayList<>(); } public void startMonitoring(long intervalSeconds) { if (monitoring) { logger.warn("USE monitoring is already running"); return; } monitoring = true; monitoringTask = scheduler.scheduleAtFixedRate( this::collectAndAnalyze, 0, intervalSeconds, TimeUnit.SECONDS ); logger.info("Started USE monitoring with {} second interval", intervalSeconds); } public void stopMonitoring() { if (!monitoring) { return; } monitoring = false; if (monitoringTask != null) { monitoringTask.cancel(false); } logger.info("Stopped USE monitoring"); } public void addListener(USEMetricsListener listener) { listeners.add(listener); } public void removeListener(USEMetricsListener listener) { listeners.remove(listener); } private void collectAndAnalyze() { try { // Discover resources List<SystemResource> resources = resourceDiscoverer.discoverResources(); // Collect metrics Map<SystemResource, USEMetrics> metrics = metricCollector.collectMetrics(resources); // Analyze for bottlenecks List<Bottleneck> bottlenecks = analysisEngine.analyze(metrics); // Notify listeners notifyListeners(metrics, bottlenecks); // Log critical issues logCriticalBottlenecks(bottlenecks); } catch (Exception e) { logger.error("Error during USE monitoring cycle", e); } } private void notifyListeners(Map<SystemResource, USEMetrics> metrics, List<Bottleneck> bottlenecks) { for (USEMetricsListener listener : listeners) { try { listener.onMetricsCollected(metrics, bottlenecks); } catch (Exception e) { logger.warn("Error notifying metrics listener", e); } } } private void logCriticalBottlenecks(List<Bottleneck> bottlenecks) { List<Bottleneck> criticalBottlenecks = bottlenecks.stream() .filter(b -> b.getSeverity() == Severity.CRITICAL) .collect(Collectors.toList()); if (!criticalBottlenecks.isEmpty()) { logger.warn("Found {} critical bottlenecks:", criticalBottlenecks.size()); for (Bottleneck bottleneck : criticalBottlenecks) { logger.warn("CRITICAL: {} - {}", bottleneck.getResource().getName(), bottleneck.getDescription()); } } } @PreDestroy public void shutdown() { stopMonitoring(); scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } } } public interface USEMetricsListener { void onMetricsCollected(Map<SystemResource, USEMetrics> metrics, List<Bottleneck> bottlenecks); } @Component public class AlertingListener implements USEMetricsListener { private static final Logger logger = LoggerFactory.getLogger(AlertingListener.class); private final AlertService alertService; private final Set<String> notifiedBottlenecks = ConcurrentHashMap.newKeySet(); public AlertingListener(AlertService alertService) { this.alertService = alertService; } @Override public void onMetricsCollected(Map<SystemResource, USEMetrics> metrics, List<Bottleneck> bottlenecks) { for (Bottleneck bottleneck : bottlenecks) { if (bottleneck.getSeverity() == Severity.CRITICAL || bottleneck.getSeverity() == Severity.HIGH) { String bottleneckKey = createBottleneckKey(bottleneck); if (!notifiedBottlenecks.contains(bottleneckKey)) { sendAlert(bottleneck); notifiedBottlenecks.add(bottleneckKey); } } } // Clean up resolved bottlenecks Set<String> currentBottleneckKeys = bottlenecks.stream() .filter(b -> b.getSeverity() == Severity.CRITICAL || b.getSeverity() == Severity.HIGH) .map(this::createBottleneckKey) .collect(Collectors.toSet()); notifiedBottlenecks.retainAll(currentBottleneckKeys); } private String createBottleneckKey(Bottleneck bottleneck) { return bottleneck.getResource().getId() + ":" + bottleneck.getType(); } private void sendAlert(Bottleneck bottleneck) { Alert alert = new Alert( "USE Method Bottleneck Detected", String.format("Critical bottleneck detected in %s: %s", bottleneck.getResource().getName(), bottleneck.getDescription()), bottleneck.getSeverity() == Severity.CRITICAL ? AlertLevel.CRITICAL : AlertLevel.HIGH, bottleneck.getRecommendation() ); alertService.sendAlert(alert); logger.warn("Sent alert for bottleneck: {}", bottleneck.getDescription()); } }

Integration with Application Performance Monitoring

Micrometer Integration

@Component public class MicrometerUSEIntegration { private final MeterRegistry meterRegistry; private final USEMonitor useMonitor; public MicrometerUSEIntegration(MeterRegistry meterRegistry, USEMonitor useMonitor) { this.meterRegistry = meterRegistry; this.useMonitor = useMonitor; setupMetricsPublishing(); } private void setupMetricsPublishing() { useMonitor.addListener((metrics, bottlenecks) -> { publishUSEMetrics(metrics); publishBottleneckMetrics(bottlenecks); }); } private void publishUSEMetrics(Map<SystemResource, USEMetrics> metrics) { for (Map.Entry<SystemResource, USEMetrics> entry : metrics.entrySet()) { SystemResource resource = entry.getKey(); USEMetrics useMetrics = entry.getValue(); // Publish utilization gauge Gauge.builder("use.utilization", useMetrics, USEMetrics::getUtilization) .tag("resource", resource.getId()) .tag("resource_type", resource.getType().name()) .register(meterRegistry); // Publish saturation gauge Gauge.builder("use.saturation", useMetrics, USEMetrics::getSaturation) .tag("resource", resource.getId()) .tag("resource_type", resource.getType().name()) .register(meterRegistry); // Publish errors counter Counter.builder("use.errors") .tag("resource", resource.getId()) .tag("resource_type", resource.getType().name()) .register(meterRegistry) .increment(useMetrics.getErrors()); } } private void publishBottleneckMetrics(List<Bottleneck> bottlenecks) { // Count bottlenecks by severity and type Map<Severity, Long> bottlenecksBySeverity = bottlenecks.stream() .collect(Collectors.groupingBy(Bottleneck::getSeverity, Collectors.counting())); Map<BottleneckType, Long> bottlenecksByType = bottlenecks.stream() .collect(Collectors.groupingBy(Bottleneck::getType, Collectors.counting())); // Publish bottleneck counts bottlenecksBySeverity.forEach((severity, count) -> { Gauge.builder("use.bottlenecks.count") .tag("severity", severity.name()) .register(meterRegistry) .set(count); }); bottlenecksByType.forEach((type, count) -> { Gauge.builder("use.bottlenecks.by_type") .tag("type", type.name()) .register(meterRegistry) .set(count); }); } }

Web Dashboard

REST API for USE Metrics

@RestController @RequestMapping("/api/use") public class USEMetricsController { private final USEMonitor useMonitor; private final ResourceDiscoverer resourceDiscoverer; private final MetricCollector metricCollector; private final AnalysisEngine analysisEngine; public USEMetricsController(USEMonitor useMonitor, ResourceDiscoverer resourceDiscoverer, MetricCollector metricCollector, AnalysisEngine analysisEngine) { this.useMonitor = useMonitor; this.resourceDiscoverer = resourceDiscoverer; this.metricCollector = metricCollector; this.analysisEngine = analysisEngine; } @GetMapping("/resources") public List<SystemResource> getResources() { return resourceDiscoverer.discoverResources(); } @GetMapping("/metrics") public Map<String, Object> getCurrentMetrics() { List<SystemResource> resources = resourceDiscoverer.discoverResources(); Map<SystemResource, USEMetrics> metrics = metricCollector.collectMetrics(resources); List<Bottleneck> bottlenecks = analysisEngine.analyze(metrics); Map<String, Object> response = new HashMap<>(); response.put("timestamp", System.currentTimeMillis()); response.put("metrics", convertMetricsToMap(metrics)); response.put("bottlenecks", bottlenecks); response.put("summary", createSummary(metrics, bottlenecks)); return response; } @GetMapping("/bottlenecks") public List<Bottleneck> getCurrentBottlenecks() { List<SystemResource> resources = resourceDiscoverer.discoverResources(); Map<SystemResource, USEMetrics> metrics = metricCollector.collectMetrics(resources); return analysisEngine.analyze(metrics); } @PostMapping("/monitoring/start") public ResponseEntity<String> startMonitoring(@RequestParam(defaultValue = "30") long interval) { useMonitor.startMonitoring(interval); return ResponseEntity.ok("USE monitoring started with " + interval + " second interval"); } @PostMapping("/monitoring/stop") public ResponseEntity<String> stopMonitoring() { useMonitor.stopMonitoring(); return ResponseEntity.ok("USE monitoring stopped"); } @GetMapping("/health") public Map<String, Object> getHealthStatus() { List<SystemResource> resources = resourceDiscoverer.discoverResources(); Map<SystemResource, USEMetrics> metrics = metricCollector.collectMetrics(resources); List<Bottleneck> bottlenecks = analysisEngine.analyze(metrics); long criticalBottlenecks = bottlenecks.stream() .filter(b -> b.getSeverity() == Severity.CRITICAL) .count(); Map<String, Object> health = new HashMap<>(); health.put("status", criticalBottlenecks > 0 ? "UNHEALTHY" : "HEALTHY"); health.put("critical_bottlenecks", criticalBottlenecks); health.put("total_resources", resources.size()); health.put("timestamp", System.currentTimeMillis()); return health; } private Map<String, Object> convertMetricsToMap(Map<SystemResource, USEMetrics> metrics) { return metrics.entrySet().stream() .collect(Collectors.toMap( entry -> entry.getKey().getId(), entry -> { USEMetrics useMetrics = entry.getValue(); Map<String, Object> metricMap = new HashMap<>(); metricMap.put("utilization", useMetrics.getUtilization()); metricMap.put("saturation", useMetrics.getSaturation()); metricMap.put("errors", useMetrics.getErrors()); metricMap.put("timestamp", useMetrics.getTimestamp()); metricMap.put("additional_metrics", useMetrics.getAdditionalMetrics()); return metricMap; } )); } private Map<String, Object> createSummary(Map<SystemResource, USEMetrics> metrics, List<Bottleneck> bottlenecks) { Map<String, Object> summary = new HashMap<>(); // Resource counts by type Map<ResourceType, Long> resourcesByType = metrics.keySet().stream() .collect(Collectors.groupingBy(SystemResource::getType, Collectors.counting())); summary.put("resources_by_type", resourcesByType); // Bottleneck counts by severity Map<Severity, Long> bottlenecksBySeverity = bottlenecks.stream() .collect(Collectors.groupingBy(Bottleneck::getSeverity, Collectors.counting())); summary.put("bottlenecks_by_severity", bottlenecksBySeverity); // Average utilization double avgUtilization = metrics.values().stream() .mapToDouble(USEMetrics::getUtilization) .average() .orElse(0.0); summary.put("average_utilization", avgUtilization); // Max utilization double maxUtilization = metrics.values().stream() .mapToDouble(USEMetrics::getUtilization) .max() .orElse(0.0); summary.put("max_utilization", maxUtilization); return summary; } }

Testing Framework

USE Method Test Suite

@SpringBootTest @TestPropertySource(properties = { "use.monitoring.enabled=true", "use.monitoring.interval=30" }) public class USEMethodTest { @Autowired private ResourceDiscoverer resourceDiscoverer; @Autowired private MetricCollector metricCollector; @Autowired private AnalysisEngine analysisEngine; @Autowired private USEMonitor useMonitor; @Test void testResourceDiscovery() { List<SystemResource> resources = resourceDiscoverer.discoverResources(); assertFalse(resources.isEmpty(), "Should discover at least one resource"); // Verify CPU resources are discovered boolean hasCPU = resources.stream() .anyMatch(r -> r.getType() == ResourceType.CPU); assertTrue(hasCPU, "Should discover CPU resources"); // Verify memory resources are discovered boolean hasMemory = resources.stream() .anyMatch(r -> r.getType() == ResourceType.MEMORY); assertTrue(hasMemory, "Should discover memory resources"); } @Test void testMetricCollection() { List<SystemResource> resources = resourceDiscoverer.discoverResources(); Map<SystemResource, USEMetrics> metrics = metricCollector.collectMetrics(resources); assertEquals(resources.size(), metrics.size(), "Should collect metrics for all discovered resources"); // Verify metrics have valid values for (USEMetrics metric : metrics.values()) { assertTrue(metric.getUtilization() >= 0.0 && metric.getUtilization() <= 1.0, "Utilization should be between 0 and 1"); assertTrue(metric.getSaturation() >= 0.0, "Saturation should be non-negative"); assertTrue(metric.getErrors() >= 0, "Errors should be non-negative"); } } @Test void testBottleneckDetection() { // Create test resources with high utilization SystemResource highCpuResource = new SystemResource.Builder() .id("test-cpu-high") .name("Test CPU High") .type(ResourceType.CPU) .build(); USEMetrics highCpuMetrics = new USEMetrics( highCpuResource, 0.95, 5.0, 0, System.currentTimeMillis(), null); Map<SystemResource, USEMetrics> testMetrics = new HashMap<>(); testMetrics.put(highCpuResource, highCpuMetrics); List<Bottleneck> bottlenecks = analysisEngine.analyze(testMetrics); assertFalse(bottlenecks.isEmpty(), "Should detect bottlenecks for high utilization"); Bottleneck cpuBottleneck = bottlenecks.get(0); assertEquals(BottleneckType.UTILIZATION, cpuBottleneck.getType()); assertEquals(Severity.CRITICAL, cpuBottleneck.getSeverity()); } @Test void testMonitoringLifecycle() throws InterruptedException { // Test start/stop monitoring useMonitor.startMonitoring(1); // 1 second interval Thread.sleep(1500); // Wait for one collection cycle useMonitor.stopMonitoring(); // Verify no exceptions occurred during monitoring assertTrue(true, "Monitoring should run without exceptions"); } @Test void testErrorConditions() { // Test with invalid resource SystemResource invalidResource = new SystemResource.Builder() .id("invalid") .name("Invalid Resource") .type(ResourceType.CPU) .build(); // This should not throw an exception USEMetrics metrics = metricCollector.collectResourceMetrics(invalidResource); assertNotNull(metrics, "Should handle invalid resources gracefully"); } }

Performance Test with Saturation

@SpringBootTest public class SaturationTest { @Autowired private USEMonitor useMonitor; @Test void testUnderLoad() throws InterruptedException { // Start monitoring useMonitor.startMonitoring(1); // Create CPU load createCPULoad(5000); // 5 seconds of load // Wait for monitoring to detect Thread.sleep(3000); // Stop monitoring useMonitor.stopMonitoring(); // Verify high CPU utilization was detected // (This would typically verify through listeners or stored metrics) } private void createCPULoad(long durationMs) { ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); long endTime = System.currentTimeMillis() + durationMs; for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) { executor.submit(() -> { while (System.currentTimeMillis() < endTime) { // Busy wait to consume CPU Math.sqrt(Math.random() * 1000000); } }); } executor.shutdown(); try { executor.awaitTermination(durationMs + 1000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }

Configuration

Spring Boot Configuration

@Configuration @EnableConfigurationProperties(USEMonitoringProperties.class) public class USEMonitoringConfig { @Bean @ConditionalOnProperty(name = "use.monitoring.enabled", havingValue = "true") public ResourceDiscoverer resourceDiscoverer() { return new ResourceDiscoverer(); } @Bean @ConditionalOnProperty(name = "use.monitoring.enabled", havingValue = "true") public MetricCollector metricCollector(MeterRegistry meterRegistry) { return new MetricCollector(meterRegistry); } @Bean @ConditionalOnProperty(name = "use.monitoring.enabled", havingValue = "true") public AnalysisEngine analysisEngine(USEMonitoringProperties properties) { return new AnalysisEngine( properties.getUtilizationThreshold(), properties.getSaturationThreshold() ); } @Bean @ConditionalOnProperty(name = "use.monitoring.enabled", havingValue = "true") public USEMonitor useMonitor(ResourceDiscoverer resourceDiscoverer, MetricCollector metricCollector, AnalysisEngine analysisEngine) { return new USEMonitor(resourceDiscoverer, metricCollector, analysisEngine); } @Bean @ConditionalOnProperty(name = "use.monitoring.enabled", havingValue = "true") public MicrometerUSEIntegration micrometerUSEIntegration(MeterRegistry meterRegistry, USEMonitor useMonitor) { return new MicrometerUSEIntegration(meterRegistry, useMonitor); } } @ConfigurationProperties(prefix = "use.monitoring") @Data public class USEMonitoringProperties { private boolean enabled = false; private long interval = 30; // seconds private double utilizationThreshold = 0.8; private double saturationThreshold = 10.0; private Alerting alerting = new Alerting(); @Data public static class Alerting { private boolean enabled = true; private Severity minimumSeverity = Severity.HIGH; private List<String> notificationChannels = Arrays.asList("LOG"); } }

Conclusion

This comprehensive USE Method implementation provides:

  1. Resource Discovery - Automatic identification of system resources
  2. Metric Collection - Utilization, saturation, and error metrics for all resources
  3. Bottleneck Detection - Intelligent analysis of performance bottlenecks
  4. Real-time Monitoring - Continuous monitoring with configurable intervals
  5. Alerting - Proactive notification of critical issues
  6. Integration - Micrometer integration for existing monitoring systems
  7. REST API - Web interface for metrics and analysis
  8. Testing - Comprehensive test suite for validation

The USE Method helps systematically identify performance bottlenecks by focusing on the three key metrics for every resource, enabling targeted optimization and capacity planning.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper