From 5dac131a9e5f0ce85346df5b8f7195e1af4f8b51 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Wed, 2 Aug 2023 10:20:06 +1000 Subject: [PATCH] Remove Old Dev UI: Core Build Metrics Signed-off-by: Phillip Kruger --- .../BuildMetricsDevConsoleProcessor.java | 285 ------------------ .../main/resources/dev-templates/index.html | 22 +- .../build-items.html | 34 --- .../build-step-dependency-graph.html | 157 ---------- .../build-steps.html | 211 ------------- 5 files changed, 1 insertion(+), 708 deletions(-) delete mode 100644 extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/BuildMetricsDevConsoleProcessor.java delete mode 100644 extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-items.html delete mode 100644 extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-step-dependency-graph.html delete mode 100644 extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-steps.html diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/BuildMetricsDevConsoleProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/BuildMetricsDevConsoleProcessor.java deleted file mode 100644 index a244accd8224e..0000000000000 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/BuildMetricsDevConsoleProcessor.java +++ /dev/null @@ -1,285 +0,0 @@ -package io.quarkus.vertx.http.deployment.devmode; - -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Supplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.impl.LazyValue; -import io.quarkus.deployment.IsDevelopment; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; -import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem; -import io.quarkus.vertx.http.deployment.devmode.BuildMetricsDevConsoleProcessor.DependecyGraph.Link; -import io.quarkus.vertx.http.deployment.devmode.BuildMetricsDevConsoleProcessor.DependecyGraph.Node; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; - -@Deprecated(forRemoval = true) -public class BuildMetricsDevConsoleProcessor { - - private static final Logger LOG = Logger.getLogger(BuildMetricsDevConsoleProcessor.class.getName()); - - @BuildStep(onlyIf = IsDevelopment.class) - DevConsoleTemplateInfoBuildItem collectMetrics(BuildSystemTargetBuildItem buildSystemTarget) { - return new DevConsoleTemplateInfoBuildItem("buildMetrics", - // We need to read the data lazily because the build is not finished yet at the time this build item is produced - // This also means that no parsing is done until the build steps view is actually used - new LazyValue>(new Supplier>() { - - @Override - public Map get() { - Map metrics = new HashMap<>(); - Map stepIdToRecord = new HashMap<>(); - Map recordIdToRecord = new HashMap<>(); - Map> threadToRecords = new HashMap<>(); - long buildDuration = 0; - LocalTime buildStarted = null; - - Path metricsJsonFile = buildSystemTarget.getOutputDirectory().resolve("build-metrics.json"); - if (Files.isReadable(metricsJsonFile)) { - try { - JsonObject data = new JsonObject(Files.readString(metricsJsonFile)); - buildDuration = data.getLong("duration"); - buildStarted = LocalDateTime - .parse(data.getString("started"), DateTimeFormatter.ISO_LOCAL_DATE_TIME).toLocalTime(); - - JsonArray records = data.getJsonArray("records"); - for (Object record : records) { - JsonObject recordObj = (JsonObject) record; - recordObj.put("encodedStepId", URLEncoder.encode(recordObj.getString("stepId"), - StandardCharsets.UTF_8.toString())); - String thread = recordObj.getString("thread"); - stepIdToRecord.put(recordObj.getString("stepId"), recordObj); - recordIdToRecord.put(recordObj.getInteger("id"), recordObj); - List steps = threadToRecords.get(thread); - if (steps == null) { - steps = new ArrayList<>(); - threadToRecords.put(thread, steps); - } - steps.add(recordObj); - } - - metrics.put("records", records); - metrics.put("items", data.getJsonArray("items")); - metrics.put("itemsCount", data.getInteger("itemsCount")); - metrics.put("duration", buildDuration); - } catch (IOException e) { - LOG.error(e); - } - } - - // Build dependency graphs - Map dependencyGraphs = new HashMap<>(); - for (Map.Entry e : stepIdToRecord.entrySet()) { - dependencyGraphs.put(e.getKey(), - buildDependencyGraph(e.getValue(), stepIdToRecord, recordIdToRecord)); - } - metrics.put("dependencyGraphs", dependencyGraphs); - - // Time slots - long slotDuration = Math.max(10, buildDuration / 100); - List slots = new ArrayList<>(); - long currentSlot = slotDuration; - while (currentSlot < buildDuration) { - slots.add(currentSlot); - currentSlot += slotDuration; - } - if (currentSlot != buildDuration) { - slots.add(buildDuration); - } - metrics.put("slots", slots); - - Map>> threadToSlotRecords = new HashMap<>(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS"); - - for (Map.Entry> entry : threadToRecords.entrySet()) { - String thread = entry.getKey(); - List records = entry.getValue(); - List> threadSlots = new ArrayList<>(); - - for (Long slot : slots) { - List slotRecords = new ArrayList<>(); - for (JsonObject record : records) { - LocalTime started = LocalTime.parse(record.getString("started"), formatter); - long startAt = Duration.between(buildStarted, started).toMillis(); - if (startAt < slot && (slot - slotDuration) < (startAt + record.getLong("duration"))) { - slotRecords.add(record.getString("stepId")); - } - } - threadSlots.add(slotRecords); - } - threadToSlotRecords.put(thread, threadSlots); - } - metrics.put("threadSlotRecords", threadToSlotRecords); - - return metrics; - } - })); - - } - - DependecyGraph buildDependencyGraph(JsonObject step, Map stepIdToRecord, - Map recordIdToRecord) { - Set nodes = new HashSet<>(); - Set links = new HashSet<>(); - - addNodesDependents(step, nodes, links, step, stepIdToRecord, recordIdToRecord); - addNodeDependencies(step, nodes, links, step, stepIdToRecord, recordIdToRecord); - return new DependecyGraph(nodes, links); - } - - void addNodesDependents(JsonObject root, Set nodes, Set links, JsonObject record, - Map stepIdToRecord, Map recordIdToRecord) { - String stepId = record.getString("stepId"); - nodes.add(new Node(stepId, record.getString("encodedStepId"))); - for (Object dependentRecordId : record.getJsonArray("dependents")) { - int recordId = (int) dependentRecordId; - if (recordId != record.getInteger("id")) { - JsonObject dependentRecord = recordIdToRecord.get(recordId); - String dependentStepId = dependentRecord.getString("stepId"); - links.add(Link.dependent(root.equals(record), dependentStepId, stepId)); - nodes.add(new Node(dependentStepId, dependentRecord.getString("encodedStepId"))); - // NOTE: we do not fetch transient dependencies yet because the UI is not ready to show so many nodes - // if (added) { - // addNodesDependents(root, nodes, links, dependentRecord, stepIdToRecord, recordIdToRecord); - // } - } - } - } - - void addNodeDependencies(JsonObject root, Set nodes, Set links, JsonObject record, - Map stepIdToRecord, Map recordIdToRecord) { - for (Map.Entry entry : stepIdToRecord.entrySet()) { - for (Object dependentRecordId : entry.getValue().getJsonArray("dependents")) { - int recordId = (int) dependentRecordId; - if (record.getInteger("id") == recordId) { - links.add(Link.dependency(root.equals(record), - record.getString("stepId"), entry.getValue().getString("stepId"))); - nodes.add(new Node(entry.getValue().getString("stepId"), entry.getValue().getString("encodedStepId"))); - // NOTE: we do not fetch transient dependencies yet because the UI is not ready to show so many nodes - // if (added) { - // addNodeDependencies(root, nodes, links, entry.getValue(), stepIdToRecord, recordIdToRecord); - // } - } - } - } - } - - public static class DependecyGraph { - - public final Set nodes; - public final Set links; - - public DependecyGraph(Set nodes, Set links) { - this.nodes = nodes; - this.links = links; - } - - public static class Node { - - public final String stepId; - public final String simpleName; - public final String encodedStepId; - - public Node(String stepId, String encodedStepId) { - this.stepId = stepId; - this.encodedStepId = encodedStepId; - int lastDot = stepId.lastIndexOf('.'); - String simple = lastDot > 0 ? stepId.substring(lastDot + 1) : stepId; - int hash = simple.indexOf('#'); - if (hash > 0) { - StringBuilder sb = new StringBuilder(); - char[] chars = simple.substring(0, hash).toCharArray(); - for (char c : chars) { - if (Character.isUpperCase(c)) { - sb.append(c); - } - } - simple = sb + simple.substring(hash); - } - this.simpleName = simple; - } - - @Override - public int hashCode() { - return Objects.hash(stepId); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Node other = (Node) obj; - return Objects.equals(stepId, other.stepId); - } - - } - - public static class Link { - - static Link dependent(boolean direct, String source, String target) { - return new Link(source, target, direct ? "directDependent" : "dependency"); - } - - static Link dependency(boolean direct, String source, String target) { - return new Link(source, target, direct ? "directDependency" : "dependency"); - } - - public final String source; - public final String target; - public final String type; - - public Link(String source, String target, String type) { - this.source = source; - this.target = target; - this.type = type; - } - - @Override - public int hashCode() { - return Objects.hash(source, target); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Link other = (Link) obj; - return Objects.equals(source, other.source) && Objects.equals(target, other.target); - } - - } - - } - -} diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html index a3de5675e993c..d90b97be3e743 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html +++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html @@ -3,27 +3,7 @@ {#body}
-
-
-
- Build -
- -
-
+
diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-items.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-items.html deleted file mode 100644 index c194a34652a1c..0000000000000 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-items.html +++ /dev/null @@ -1,34 +0,0 @@ -{#let metrics=info:buildMetrics.get} - -{#include main fluid=true} -{#title}Build Items{/title} -{#body} -

-Produced {metrics.itemsCount} build items. -

- - - - - - - - - - - {#for item in metrics.items} - - - - - {/for} - -
#Build ItemCount
{item_count} - {item.class} - - {item.count} -
- -{/include} - -{/let} \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-step-dependency-graph.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-step-dependency-graph.html deleted file mode 100644 index 5d1021effe55b..0000000000000 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-step-dependency-graph.html +++ /dev/null @@ -1,157 +0,0 @@ -{#include main fluid=true} - - {#script} - // Build step dependency graph built with d3.js - // Based on https://observablehq.com/@d3/mobile-patent-suits - - const stepId = "{currentRequest.getParam('stepId')}"; - - const nodes = [ - {#each info:buildMetrics.get.dependencyGraphs.get(currentRequest.getParam('stepId')).nodes} - { id:"{it.stepId}", root:{#if it.stepId == currentRequest.getParam('stepId')}true{#else}false{/if}, simpleName:"{it.simpleName}", encodedId:"{it.encodedStepId}" }, - {/each} - ]; - const links = [ - {#each info:buildMetrics.get.dependencyGraphs.get(currentRequest.getParam('stepId')).links} - { source:"{it.source}", target:"{it.target}", type:"{it.type}" }, - {/each} - ]; - - {| - const types = ['directDependency','directDependent','dependency']; - const height = 600; - const width = 1200; - const color = d3.scaleOrdinal(types, d3.schemeCategory10); - - // Legend colors - const legendDirectDependency = document.querySelector(".legend-direct-dependency"); - legendDirectDependency.style.color = color('directDependency'); - const legendDirectDependent = document.querySelector(".legend-direct-dependent"); - legendDirectDependent.style.color = color('directDependent'); - // const legendDependency = document.querySelector(".legend-dependency"); - // legendDependency.style.color = color('dependency'); - - function linkArc(d) { - const r = Math.hypot(d.target.x - d.source.x, d.target.y - d.source.y); - return ` - M${d.source.x},${d.source.y} - A${r},${r} 0 0,1 ${d.target.x},${d.target.y} - `; - } - - const simulation = d3.forceSimulation(nodes) - .force("link", d3.forceLink(links).id(d => d.id).distance(function(d) { - return d.source.id === stepId || d.type === 'directDependency' ? 125 : 255; - })) - .force("charge", d3.forceManyBody().strength(-400)) - .force("x", d3.forceX()) - .force("y", d3.forceY()); - - function dragstart(event, d){ - // this line is needed, otherwise the simulation stops after few seconds - if (!event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - }; - - function dragged(event, d) { - d.fx = event.x; - d.fy = event.y; - } - - function dragended(event, d) { - d.fx = event.x; - d.fy = event.y; - } - - const svg = d3.select("#buildStepDepGraph_area") - .attr("viewBox", [-width / 3, -height / 3, width, height]) - .style("font", "11px sans-serif"); - - svg.append("defs").selectAll("marker") - .data(types) - .join("marker") - .attr("id", d => `arrow-${d}`) - .attr("viewBox", "0 -5 10 10") - .attr("refX", 15) - .attr("refY", -0.5) - .attr("markerWidth", 6) - .attr("markerHeight", 6) - .attr("orient", "auto") - .append("path") - .attr("fill", color) - .attr("d", "M0,-5L10,0L0,5"); - - const link = svg.append("g") - .attr("fill", "none") - .attr("stroke-width", 1.5) - .selectAll("path") - .data(links) - .join("path") - .attr("stroke", d => color(d.type)) - .attr("marker-end", d => `url(${new URL(`#arrow-${d.type}`, location)})`); - - const node = svg.append("g") - .attr("fill", "currentColor") - .attr("stroke-linecap", "round") - .attr("stroke-linejoin", "round") - .selectAll("g") - .data(nodes) - .join("g") - .call(d3.drag().on("drag", dragged).on("end", dragended).on("start", dragstart)); - - node.append("circle") - .attr("stroke", "white") - .attr("stroke-width", 1) - .attr("r", 5) - .style("fill", d => d.root ? "red" : "black"); - - const anchor = node.append("a") - .attr("xlink:href", d => "build-step-dependency-graph?stepId=" + d.encodedId); - - anchor.append("svg:text") - .attr("x", 8) - .attr("y", "0.31em") - .style("fill", "#1f77b4") - .text(d => d.simpleName); - - anchor.append("svg:title") - .text(d => d.id); - - simulation.on("tick", () => { - link.attr("d", linkArc); - node.attr("transform", d => `translate(${d.x},${d.y})`); - }); - - |} - - {#breadcrumbs} Build Steps{/breadcrumbs} - {#title}Build Step Dependency Graph{/title} - {#body} - -
-
- {currentRequest.getParam('stepId')} -
-
-
-
-
    -
  • root
  • -
  • direct dependencies
  • -
  • direct dependents
  • - - -
-
-
- -
-
- - {/body} - - {#scriptref} - - {/scriptref} -{/include} diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-steps.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-steps.html deleted file mode 100644 index 565fcfdad1f6f..0000000000000 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/build-steps.html +++ /dev/null @@ -1,211 +0,0 @@ -{#let metrics=info:buildMetrics.get} - -{#include main fluid=true} -{#style} -#chartjs-tooltip { - color: white; - border-radius: 0em 1em 1em 1em; - padding: 1em; -} -.tooltip-steps { - padding-left: 0.5em; - margin-bottom: 0; - font-size: 1.2em; -} -.tooltip-title { - font-size: 1.5em; - font-weight: bold; -} -{/style} -{#title}Build Steps{/title} -{#body} -

-Executed {metrics.records.size} build steps on {metrics.threadSlotRecords.keys.size} threads in {metrics.duration} ms. -

- -

-Build Steps Concurrent Execution Chart -

- - - - - - - - - - - - - - {#for record in metrics.records} - - - - - - - - {/for} - -
#Build StepStartedDurationThreadActions
{record_count} - {record.stepId} - - {record.started} - - {record.duration} ms - - {record.thread} - - {#if !metrics.dependencyGraphs.get(record.stepId).links.empty} - - {/if} -
- -

Build Steps Concurrent Execution Chart

-
- -
- -{#script} - const labels = [ {#each metrics.slots}{it}{#if it_hasNext},{/if}{/each} ]; - const nextColor = function(number) { - const hue = number * 137.508; // golden angle approximation - return "hsl(" + hue +",80%,65%)"; - }; - - const threadBuildSteps = { - {#for entry in metrics.threadSlotRecords.entrySet} - "{entry.key}" : [ - {#for data in entry.value} - [ {#for stepId in data} '{stepId}',{/for} ], - {/for} - ], - {/for} - } - - const data = { - labels: labels, - datasets: [ - {#for entry in metrics.threadSlotRecords.entrySet} - { - label: '{entry.key}', - data: [{#each entry.value}{#if it.empty}0{#else}1{/if},{/each}], - backgroundColor: nextColor({entry_index}), - }, - {/for} - ] - }; - - const externalTooltip = (context) => { - const { chart, tooltip } = context; - let tooltipEl = document.getElementById('chartjs-tooltip'); - - // Create element on first render - if (!tooltipEl) { - tooltipEl = document.createElement('div'); - tooltipEl.id = 'chartjs-tooltip'; - chart.canvas.parentNode.appendChild(tooltipEl); - } - - // Hide if no tooltip - if (tooltip.opacity === 0) { - tooltipEl.style.opacity = 0; - return; - } - - // Set caret Position - tooltipEl.classList.remove('above', 'below', 'no-transform'); - if (tooltip.yAlign) { - tooltipEl.classList.add(tooltip.yAlign); - } else { - tooltipEl.classList.add('no-transform'); - } - - let innerHtml = ''; - // We expect a single tooltip item - const tooltipItem = context.tooltip.dataPoints[0]; - const thread = tooltipItem.dataset.label; - const buildStepIds = threadBuildSteps[thread][tooltipItem.dataIndex]; - - innerHtml += '
' + thread + '
'; - innerHtml += '
    '; - buildStepIds.forEach(function(stepId, i) { - innerHtml += '
  • '; - const lastDot = stepId.lastIndexOf('.'); - if (lastDot > 0) { - innerHtml += stepId.substring(lastDot + 1); - } else { - innerHtml += stepId; - } - innerHtml += '
  • '; - }); - innerHtml += '
'; - - let ulRoot = tooltipEl; - ulRoot.innerHTML = innerHtml; - - const position = context.chart.canvas.getBoundingClientRect(); - const bodyFont = Chart.helpers.toFont(tooltip.options.bodyFont); - - // Display, position, and font - tooltipEl.style.opacity = 1; - tooltipEl.style.position = 'absolute'; - - const { offsetLeft: positionX, offsetTop: positionY } = chart.canvas; - tooltipEl.style.left = positionX + tooltip.caretX + 'px'; - tooltipEl.style.top = (positionY + tooltip.caretY + 7) + 'px'; - - //tooltipEl.style.left = position.left + window.pageXOffset + tooltip.caretX + 'px'; - //tooltipEl.style.top = position.top + window.pageYOffset + tooltip.caretY + 'px'; - tooltipEl.style.font = bodyFont.string; - tooltipEl.style.padding = tooltip.padding + 'px ' + tooltip.padding + 'px'; - tooltipEl.style.background = 'rgba(0, 0, 0, 0.7)'; - tooltipEl.style.pointerEvents = 'none'; - }; - - const config = { - type: 'bar', - data: data, - options: { - plugins: { - title: { - display: true, - text: 'Build Step Concurrent Execution', - }, - tooltip: { - enabled: false, - external: externalTooltip, - } - }, - responsive: true, - scales: { - x: { - stacked: true, - title: { - display: true, - text: "{metrics.slots.size} time slots ({metrics.slots.get(0)} ms)", - }, - }, - y: { - stacked: true, - title: { - display: true, - text: "Number of build threads used in a time slot", - }, - } - } - } - }; - - const ctx = document.getElementById('buildStepsChart').getContext('2d'); - const buildStepsChart = new Chart(ctx, config); - -{#scriptref} - -{/include} - -{/let} \ No newline at end of file