001/*
002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.benchmarking.common.metrics;
017
018import de.cuioss.tools.logging.CuiLogger;
019
020import java.io.IOException;
021import java.nio.file.Files;
022import java.nio.file.Path;
023import java.time.Duration;
024import java.time.Instant;
025import java.util.List;
026import java.util.Map;
027
028import static de.cuioss.benchmarking.common.util.BenchmarkingLogMessages.ERROR.FAILED_COLLECT_REALTIME_PROMETHEUS;
029import static de.cuioss.benchmarking.common.util.BenchmarkingLogMessages.INFO.COLLECTING_REALTIME_METRICS;
030import static de.cuioss.benchmarking.common.util.BenchmarkingLogMessages.INFO.REALTIME_METRICS_EXPORTED;
031
032/**
033 * Orchestrator for metrics processing.
034 * Coordinates the transformation and export of real-time Prometheus metrics.
035 * Delegates all transformation logic to BenchmarkMetricsTransformer.
036 */
037public class MetricsOrchestrator {
038
039    private static final CuiLogger LOGGER = new CuiLogger(MetricsOrchestrator.class);
040
041    private final PrometheusClient prometheusClient;
042
043    /**
044     * Creates a new metrics orchestrator.
045     *
046     * @param prometheusClient PrometheusClient for real-time metrics collection
047     */
048    public MetricsOrchestrator(PrometheusClient prometheusClient) {
049        this.prometheusClient = prometheusClient;
050    }
051
052
053    /**
054     * Collects real-time metrics from Prometheus for a benchmark execution.
055     * Fetches time-series data, calculates statistics (avg, p50, p95, max),
056     * and exports to JSON format.
057     *
058     * @param benchmarkName Name of the benchmark for file naming
059     * @param startTime Start time of the benchmark execution
060     * @param endTime End time of the benchmark execution
061     * @param outputDir Directory to store the metrics JSON file
062     * @throws IOException if I/O operations fail
063     */
064    public void collectBenchmarkMetrics(String benchmarkName, Instant startTime, Instant endTime, Path outputDir)
065            throws IOException {
066        LOGGER.info(COLLECTING_REALTIME_METRICS, benchmarkName, startTime, endTime);
067
068        // Define metrics to collect during benchmark execution
069        // Using actual metric names from Prometheus
070        List<String> metricNames = List.of(
071                // CPU metrics
072                "process_cpu_usage",
073                "system_cpu_usage",
074                "system_cpu_count",
075
076                // Memory metrics
077                "jvm_memory_used_bytes",
078                "jvm_memory_committed_bytes",
079                "jvm_memory_max_bytes",
080
081                // Thread metrics
082                "jvm_threads_live_threads",
083                "jvm_threads_daemon_threads",
084                "jvm_threads_peak_threads",
085
086                // GC metrics
087                "jvm_gc_overhead",
088
089                // JWT specific metrics
090                "sheriff_oauth_validation_success_operations_total",
091                "sheriff_oauth_validation_errors_total",
092                "sheriff_oauth_bearer_token_validation_seconds_count",
093                "sheriff_oauth_bearer_token_validation_seconds_sum"
094        );
095
096        try {
097            // Query Prometheus for metrics within the benchmark time window
098            Duration step = Duration.ofSeconds(2); // 2-second resolution matching scrape interval
099            Map<String, PrometheusClient.TimeSeries> timeSeriesData =
100                    prometheusClient.queryRange(metricNames, startTime, endTime, step);
101
102            // Transform time-series data using the new BenchmarkMetricsTransformer
103            BenchmarkMetricsTransformer transformer = new BenchmarkMetricsTransformer();
104            Map<String, Object> metricsOutput = transformer.transformToServerMetrics(
105                    benchmarkName, startTime, endTime, timeSeriesData);
106
107            // Export to JSON file in the format specified in benchmark-metrics.adoc
108            Files.createDirectories(outputDir);
109            String fileName = "%s-server-metrics.json".formatted(benchmarkName);
110            MetricsJsonExporter exporter = new MetricsJsonExporter(outputDir);
111            exporter.exportToFile(fileName, metricsOutput);
112
113            Path outputFile = outputDir.resolve(fileName);
114            LOGGER.info(REALTIME_METRICS_EXPORTED, benchmarkName, outputFile);
115
116        } catch (PrometheusClient.PrometheusException e) {
117            LOGGER.error(e, FAILED_COLLECT_REALTIME_PROMETHEUS, benchmarkName, e.getMessage());
118            throw new IOException("Failed to collect Prometheus metrics", e);
119        }
120    }
121}