Mercurial > hg > heapstats
changeset 194:af656f596752
Bug 3247: Refactoring chart legends and tooltip
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/57
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Mon, 12 Dec 2016 22:04:39 +0900 |
parents | d70bd5dab532 |
children | 09afe400f30a |
files | ChangeLog analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogResourcesController.java analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SummaryController.java analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/resources.fxml analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/summary.fxml |
diffstat | 6 files changed, 401 insertions(+), 82 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Dec 07 21:12:08 2016 +0900 +++ b/ChangeLog Mon Dec 12 22:04:39 2016 +0900 @@ -1,3 +1,7 @@ +2016-12-12 Yasumasa Suenaga <yasuenag@gmail.com> + + * Bug 3247: Refactoring chart legends and tooltip + 2016-12-07 Yasumasa Suenaga <yasuenag@gmail.com> * Bug 3259: [REFACTORING] color selection for chart classes
--- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogResourcesController.java Wed Dec 07 21:12:08 2016 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogResourcesController.java Mon Dec 12 22:04:39 2016 +0900 @@ -21,10 +21,9 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; -import java.util.Optional; import java.util.ResourceBundle; -import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; @@ -32,6 +31,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.concurrent.Task; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Node; @@ -40,16 +40,18 @@ import javafx.scene.chart.NumberAxis; import javafx.scene.chart.StackedAreaChart; import javafx.scene.chart.XYChart; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; +import javafx.scene.control.Tooltip; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.StackPane; +import javafx.scene.shape.Path; import javafx.scene.shape.Rectangle; -import javafx.stage.Popup; import jp.co.ntt.oss.heapstats.container.log.ArchiveData; import jp.co.ntt.oss.heapstats.container.log.DiffData; import jp.co.ntt.oss.heapstats.container.log.LogData; @@ -129,10 +131,6 @@ @FXML private TableColumn<SummaryData.SummaryDataEntry, String> valueColumn; - private Popup chartPopup; - - private Label popupText; - private ObjectProperty<ObservableList<ArchiveData>> archiveList; private List<LocalDateTime> suspectList; @@ -141,6 +139,170 @@ private EpochTimeConverter epochTimeConverter; + /* Tooltip for Java CPU chart */ + private Tooltip javaCPUTooltip; + + private GridPane javaCPUTooltipGrid; + + private Label javaUserLabel; + + private Label javaSysLabel; + + /* Tooltip for System CPU chart */ + private Tooltip systemCPUTooltip; + + private GridPane systemCPUTooltipGrid; + + private Label systemUserLabel; + + private Label systemNiceLabel; + + private Label systemSysLabel; + + private Label systemIdleLabel; + + private Label systemIOWaitLabel; + + private Label systemIRQLabel; + + private Label systemSoftIRQLabel; + + private Label systemStealLabel; + + private Label systemGuestLabel; + + /* Tooltip for Java Memory chart */ + private Tooltip javaMemoryTooltip; + + private GridPane javaMemoryTooltipGrid; + + private Label javaMemoryVSZLabel; + + private Label javaMemoryRSSLabel; + + /* Generic Tooltip */ + private Tooltip tooltip; + + private void initializeJavaCPUTooltip(){ + javaUserLabel = new Label(); + javaSysLabel = new Label(); + + Rectangle javaUserRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle javaSysRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + javaUserRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + javaSysRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + }); + + javaCPUTooltipGrid = new GridPane(); + javaCPUTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + javaCPUTooltipGrid.add(javaUserRect, 0, 0); + javaCPUTooltipGrid.add(new Label("user"), 1, 0); + javaCPUTooltipGrid.add(javaUserLabel, 2, 0); + javaCPUTooltipGrid.add(javaSysRect, 0, 1); + javaCPUTooltipGrid.add(new Label("sys"), 1, 1); + javaCPUTooltipGrid.add(javaSysLabel, 2, 1); + + javaCPUTooltip = new Tooltip(); + javaCPUTooltip.setGraphic(javaCPUTooltipGrid); + javaCPUTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + + private void initializeJavaMemoryTooltip(){ + javaMemoryVSZLabel = new Label(); + javaMemoryRSSLabel = new Label(); + + Rectangle javaMemoryVSZRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle javaMemoryRSSRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + javaMemoryRSSRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + javaMemoryVSZRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + }); + + javaMemoryTooltipGrid = new GridPane(); + javaMemoryTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + javaMemoryTooltipGrid.add(javaMemoryVSZRect, 0, 0); + javaMemoryTooltipGrid.add(new Label("VSZ"), 1, 0); + javaMemoryTooltipGrid.add(javaMemoryVSZLabel, 2, 0); + javaMemoryTooltipGrid.add(javaMemoryRSSRect, 0, 1); + javaMemoryTooltipGrid.add(new Label("RSS"), 1, 1); + javaMemoryTooltipGrid.add(javaMemoryRSSLabel, 2, 1); + + javaMemoryTooltip = new Tooltip(); + javaMemoryTooltip.setGraphic(javaMemoryTooltipGrid); + javaMemoryTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + + private void initializeSystemCPUTooltip(){ + systemUserLabel = new Label(); + systemNiceLabel = new Label(); + systemSysLabel = new Label(); + systemIdleLabel = new Label(); + systemIOWaitLabel = new Label(); + systemIRQLabel = new Label(); + systemSoftIRQLabel = new Label(); + systemStealLabel = new Label(); + systemGuestLabel = new Label(); + + Rectangle systemUserRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemNiceRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemSysRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemIdleRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemIOWaitRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemIRQRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemSoftIRQRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemStealRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemGuestRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + systemUserRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + systemNiceRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + systemSysRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series2")).getFill().toString().replace("0x", "#")); + systemIdleRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series3")).getFill().toString().replace("0x", "#")); + systemIOWaitRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series4")).getFill().toString().replace("0x", "#")); + systemIRQRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series5")).getFill().toString().replace("0x", "#")); + systemSoftIRQRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series6")).getFill().toString().replace("0x", "#")); + systemStealRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series7")).getFill().toString().replace("0x", "#")); + systemGuestRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series8")).getFill().toString().replace("0x", "#")); + }); + + systemCPUTooltipGrid = new GridPane(); + systemCPUTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + systemCPUTooltipGrid.add(systemUserRect, 0, 0); + systemCPUTooltipGrid.add(new Label("user"), 1, 0); + systemCPUTooltipGrid.add(systemUserLabel, 2, 0); + systemCPUTooltipGrid.add(systemNiceRect, 0, 1); + systemCPUTooltipGrid.add(new Label("nice"), 1, 1); + systemCPUTooltipGrid.add(systemNiceLabel, 2, 1); + systemCPUTooltipGrid.add(systemSysRect, 0, 2); + systemCPUTooltipGrid.add(new Label("sys"), 1, 2); + systemCPUTooltipGrid.add(systemSysLabel, 2, 2); + systemCPUTooltipGrid.add(systemIdleRect, 0, 3); + systemCPUTooltipGrid.add(new Label("idle"), 1, 3); + systemCPUTooltipGrid.add(systemIdleLabel, 2, 3); + systemCPUTooltipGrid.add(systemIOWaitRect, 0, 4); + systemCPUTooltipGrid.add(new Label("iowait"), 1, 4); + systemCPUTooltipGrid.add(systemIOWaitLabel, 2, 4); + systemCPUTooltipGrid.add(systemIRQRect, 0, 5); + systemCPUTooltipGrid.add(new Label("IRQ"), 1, 5); + systemCPUTooltipGrid.add(systemIRQLabel, 2, 5); + systemCPUTooltipGrid.add(systemSoftIRQRect, 0, 6); + systemCPUTooltipGrid.add(new Label("Soft IRQ"), 1, 6); + systemCPUTooltipGrid.add(systemSoftIRQLabel, 2, 6); + systemCPUTooltipGrid.add(systemStealRect, 0, 7); + systemCPUTooltipGrid.add(new Label("steal"), 1, 7); + systemCPUTooltipGrid.add(systemStealLabel, 2, 7); + systemCPUTooltipGrid.add(systemGuestRect, 0, 8); + systemCPUTooltipGrid.add(new Label("guest"), 1, 8); + systemCPUTooltipGrid.add(systemGuestLabel, 2, 8); + + systemCPUTooltip = new Tooltip(); + systemCPUTooltip.setGraphic(systemCPUTooltipGrid); + systemCPUTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + /** * Initializes the controller class. */ @@ -158,13 +320,12 @@ initializeChartSeries(); - chartPopup = new Popup(); - popupText = new Label(); - popupText.setStyle("-fx-font-family: monospace; -fx-text-fill: white; -fx-background-color: black;"); - chartPopup.getContent().add(popupText); archiveList = new SimpleObjectProperty<>(); - epochTimeConverter = new EpochTimeConverter(); + + initializeJavaCPUTooltip(); + initializeSystemCPUTooltip(); + initializeJavaMemoryTooltip(); } /** @@ -225,60 +386,6 @@ monitorChart.getData().add(monitors); } - /** - * Show popup window with pointing data in chart. - * - * @param chart Target chart. - * @param xValue value in X Axis. - * @param event Mouse event. - * @param labelFunc Function to format label string. - */ - private void showChartPopup(XYChart<Number, ? extends Number> chart, Number xValue, MouseEvent event, Function<? super Number, String> labelFunc) { - boolean isContained = chart.getData() - .stream() - .flatMap(s -> s.getData().stream()) - .anyMatch(d -> d.getXValue().longValue() == xValue.longValue()); - if(isContained){ - String label = chart.getData() - .stream() - .map(s -> s.getName() + " = " + s.getData() - .stream() - .filter(d -> d.getXValue().longValue() == xValue.longValue()) - .map(d -> labelFunc.apply(d.getYValue())) - .findAny() - .get()) - .collect(Collectors.joining("\n")); - popupText.setText(epochTimeConverter.toString(xValue) + "\n" + label); - chartPopup.show(chart, event.getScreenX() + 15.0d, event.getScreenY() + 3.0d); - } - } - - @FXML - @SuppressWarnings("unchecked") - private void onChartMouseMoved(MouseEvent event) { - XYChart<Number, ? extends Number> chart = (XYChart<Number, ? extends Number>) event.getSource(); - NumberAxis xAxis = (NumberAxis)chart.getXAxis(); - Function<? super Number, String> labelFunc; - - if ((chart == javaCPUChart) || (chart == systemCPUChart)) { - labelFunc = d -> String.format("%.02f %%", d); - } else if (chart == javaMemoryChart) { - labelFunc = d -> String.format("%d MB", d); - } else if (chart == safepointTimeChart) { - labelFunc = d -> String.format("%d ms", d); - } else { - labelFunc = d -> d.toString(); - } - - Optional.ofNullable(chart.getXAxis().getValueForDisplay(event.getX() - xAxis.getLayoutX())) - .ifPresent(v -> showChartPopup(chart, v, event, labelFunc)); - } - - @FXML - private void onChartMouseExited(MouseEvent event) { - chartPopup.hide(); - } - private void drawLineInternal(StackPane target, List<Number> drawList, String style) { AnchorPane anchor = null; XYChart chart = null; @@ -463,6 +570,82 @@ updateProgress(); } + private void setJavaCPUChartTooltip(int idx){ + XYChart.Data<Number, Double> userNode = javaUserUsage.getData().get(idx); + XYChart.Data<Number, Double> sysNode = javaSysUsage.getData().get(idx); + + EventHandler<MouseEvent> handler = e -> { + javaCPUTooltip.setText(epochTimeConverter.toString(userNode.getXValue())); + javaUserLabel.setText(String.format("%.02f", userNode.getYValue()) + " %"); + javaSysLabel.setText(String.format("%.02f", sysNode.getYValue()) + " %"); + }; + + Tooltip.install(userNode.getNode(), javaCPUTooltip); + userNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(sysNode.getNode(), javaCPUTooltip); + sysNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setJavaMemoryChartTooltip(int idx){ + XYChart.Data<Number, Long> vszNode = javaVSZUsage.getData().get(idx); + XYChart.Data<Number, Long> rssNode = javaRSSUsage.getData().get(idx); + + EventHandler<MouseEvent> handler = e -> { + javaMemoryTooltip.setText(epochTimeConverter.toString(vszNode.getXValue())); + javaMemoryVSZLabel.setText(vszNode.getYValue() + " MB"); + javaMemoryRSSLabel.setText(rssNode.getYValue() + " MB"); + }; + + Tooltip.install(vszNode.getNode(), javaMemoryTooltip); + vszNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(rssNode.getNode(), javaMemoryTooltip); + rssNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setSystemCPUChartTooltip(int idx){ + XYChart.Data<Number, Double> userNode = systemUserUsage.getData().get(idx); + XYChart.Data<Number, Double> niceNode = systemNiceUsage.getData().get(idx); + XYChart.Data<Number, Double> sysNode = systemSysUsage.getData().get(idx); + XYChart.Data<Number, Double> idleNode = systemIdleUsage.getData().get(idx); + XYChart.Data<Number, Double> iowaitNode = systemIOWaitUsage.getData().get(idx); + XYChart.Data<Number, Double> irqNode = systemIRQUsage.getData().get(idx); + XYChart.Data<Number, Double> softIrqNode = systemSoftIRQUsage.getData().get(idx); + XYChart.Data<Number, Double> stealNode = systemStealUsage.getData().get(idx); + XYChart.Data<Number, Double> guestNode = systemGuestUsage.getData().get(idx); + + EventHandler<MouseEvent> handler = e -> { + systemCPUTooltip.setText(epochTimeConverter.toString(userNode.getXValue())); + systemUserLabel.setText(String.format("%.02f", userNode.getYValue()) + " %"); + systemNiceLabel.setText(String.format("%.02f", niceNode.getYValue()) + " %"); + systemSysLabel.setText(String.format("%.02f", sysNode.getYValue()) + " %"); + systemIdleLabel.setText(String.format("%.02f", idleNode.getYValue()) + " %"); + systemIOWaitLabel.setText(String.format("%.02f", iowaitNode.getYValue()) + " %"); + systemIRQLabel.setText(String.format("%.02f", irqNode.getYValue()) + " %"); + systemSoftIRQLabel.setText(String.format("%.02f", softIrqNode.getYValue()) + " %"); + systemStealLabel.setText(String.format("%.02f", stealNode.getYValue()) + " %"); + systemGuestLabel.setText(String.format("%.02f", guestNode.getYValue()) + " %"); + }; + + Tooltip.install(userNode.getNode(), systemCPUTooltip); + userNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(niceNode.getNode(), systemCPUTooltip); + niceNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(sysNode.getNode(), systemCPUTooltip); + sysNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(idleNode.getNode(), systemCPUTooltip); + idleNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(iowaitNode.getNode(), systemCPUTooltip); + iowaitNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(irqNode.getNode(), systemCPUTooltip); + irqNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(softIrqNode.getNode(), systemCPUTooltip); + softIrqNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(stealNode.getNode(), systemCPUTooltip); + stealNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(guestNode.getNode(), systemCPUTooltip); + guestNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + private void setChartData() { /* Set chart range */ long startLogEpoch = targetLogData.get(0).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); @@ -504,6 +687,22 @@ threads.setData(threadsBuf); + /* Tooltip setting */ + IntStream.range(0, targetDiffData.size()) + .peek(this::setJavaCPUChartTooltip) + .forEach(this::setSystemCPUChartTooltip); + IntStream.range(0, targetLogData.size()) + .forEach(this::setJavaMemoryChartTooltip); + Tooltip tooltip = new Tooltip(); + Stream.of(threads, safepoints, monitors) + .flatMap(c -> c.getData().stream()) + .peek(d -> Tooltip.install(d.getNode(), tooltip)) + .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText((epochTimeConverter.toString(d.getXValue()) + ": " + d.getYValue())))); + safepointTime.getData() + .stream() + .peek(d -> Tooltip.install(d.getNode(), tooltip)) + .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText(String.format("%s: %d ms", epochTimeConverter.toString(d.getXValue()), d.getYValue())))); + /* Put summary data to table */ SummaryData summary = new SummaryData(targetLogData, targetDiffData); procSummary.setItems(FXCollections.observableArrayList(new SummaryData.SummaryDataEntry(resource.getString("summary.cpu.average"), String.format("%.1f %%", summary.getAverageCPUUsage())),
--- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SummaryController.java Wed Dec 07 21:12:08 2016 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SummaryController.java Mon Dec 12 22:04:39 2016 +0900 @@ -23,6 +23,7 @@ import java.util.ResourceBundle; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; @@ -31,6 +32,7 @@ import javafx.collections.ObservableList; import javafx.collections.ObservableSet; import javafx.concurrent.Task; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.chart.AreaChart; @@ -38,12 +40,18 @@ import javafx.scene.chart.NumberAxis; import javafx.scene.chart.StackedAreaChart; import javafx.scene.chart.XYChart; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.Tooltip; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Path; +import javafx.scene.shape.Rectangle; import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; import jp.co.ntt.oss.heapstats.container.snapshot.SummaryData; import jp.co.ntt.oss.heapstats.utils.EpochTimeConverter; @@ -97,7 +105,23 @@ private EpochTimeConverter epochTimeConverter; - private Tooltip tooltip; + private Tooltip heapTooltip; + + private GridPane heapTooltipGrid; + + private Label youngLabel; + + private Label oldLabel; + + private Label freeLabel; + + private Tooltip metaspaceTooltip; + + private GridPane metaspaceTooltipGrid; + + private Label metaspaceUsageLabel; + + private Label metaspaceCapacityLabel; /** * Initializes the controller class. @@ -149,8 +173,6 @@ */ @SuppressWarnings("unchecked") private void initializeChartSeries() { - tooltip = new Tooltip(); - youngUsage = new XYChart.Series<>(); youngUsage.setName("Young"); oldUsage = new XYChart.Series<>(); @@ -181,6 +203,52 @@ metaspaceUsage = new XYChart.Series<>(); metaspaceUsage.setName("Usage"); metaspaceChart.getData().addAll(metaspaceCapacity, metaspaceUsage); + + /* Tooltip setup */ + /* Java heap */ + heapTooltipGrid = new GridPane(); + heapTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + youngLabel = new Label(); + oldLabel = new Label(); + freeLabel = new Label(); + Rectangle youngRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, Color.web(heapChartColors[0])); + Rectangle oldRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, Color.web(heapChartColors[1])); + Rectangle freeRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, Color.web(heapChartColors[2])); + heapTooltipGrid.add(youngRect, 0, 0); + heapTooltipGrid.add(new Label("Young"), 1, 0); + heapTooltipGrid.add(youngLabel, 2, 0); + heapTooltipGrid.add(oldRect, 0, 1); + heapTooltipGrid.add(new Label("Old"), 1, 1); + heapTooltipGrid.add(oldLabel, 2, 1); + heapTooltipGrid.add(freeRect, 0, 2); + heapTooltipGrid.add(new Label("Free"), 1, 2); + heapTooltipGrid.add(freeLabel, 2, 2); + heapTooltip = new Tooltip(); + heapTooltip.setGraphic(heapTooltipGrid); + heapTooltip.setContentDisplay(ContentDisplay.BOTTOM); + + /* Metaspace */ + metaspaceTooltipGrid = new GridPane(); + metaspaceTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + metaspaceUsageLabel = new Label(); + metaspaceCapacityLabel = new Label(); + Rectangle metaUsageRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle metaCapacityRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + metaUsageRect.setStyle("-fx-fill: " + ((Path)metaspaceChart.lookup(".default-color0.chart-series-area-fill")).getFill().toString().replace("0x", "#")); + metaCapacityRect.setStyle("-fx-fill: " + ((Path)metaspaceChart.lookup(".default-color1.chart-series-area-fill")).getFill().toString().replace("0x", "#")); + }); + + metaspaceTooltipGrid.add(metaUsageRect, 0, 0); + metaspaceTooltipGrid.add(new Label("Usage"), 1, 0); + metaspaceTooltipGrid.add(metaspaceUsageLabel, 2, 0); + metaspaceTooltipGrid.add(metaCapacityRect, 0, 1); + metaspaceTooltipGrid.add(new Label("Capacity"), 1, 1); + metaspaceTooltipGrid.add(metaspaceCapacityLabel, 2, 1); + metaspaceTooltip = new Tooltip(); + metaspaceTooltip.setGraphic(metaspaceTooltipGrid); + metaspaceTooltip.setContentDisplay(ContentDisplay.BOTTOM); } /** @@ -257,6 +325,47 @@ return null; } + private void setupJavaHeapChartTooltip(int idx){ + XYChart.Data<Number, Long> youngNode = youngUsage.getData().get(idx); + XYChart.Data<Number, Long> oldNode = oldUsage.getData().get(idx); + XYChart.Data<Number, Long> freeNode = free.getData().get(idx); + long youngInMB = youngNode.getYValue(); + long oldInMB = oldNode.getYValue(); + long freeInMB = freeNode.getYValue(); + + EventHandler<MouseEvent> handler = e -> { + heapTooltip.setText(epochTimeConverter.toString(youngNode.getXValue())); + youngLabel.setText(youngInMB + " MB"); + oldLabel.setText(oldInMB + " MB"); + freeLabel.setText(freeInMB + " MB"); + }; + + Tooltip.install(youngNode.getNode(), heapTooltip); + youngNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(oldNode.getNode(), heapTooltip); + oldNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(freeNode.getNode(), heapTooltip); + freeNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setupMetaspaceChartTooltip(int idx){ + XYChart.Data<Number, Long> usageNode = metaspaceUsage.getData().get(idx); + XYChart.Data<Number, Long> capacityNode = metaspaceCapacity.getData().get(idx); + long usage = usageNode.getYValue(); + long capacity = capacityNode.getYValue(); + + EventHandler<MouseEvent> handler = e -> { + metaspaceTooltip.setText(epochTimeConverter.toString(usageNode.getXValue())); + metaspaceUsageLabel.setText(usage + " MB"); + metaspaceCapacityLabel.setText(capacity + " MB"); + }; + + Tooltip.install(usageNode.getNode(), metaspaceTooltip); + usageNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(capacityNode.getNode(), metaspaceTooltip); + capacityNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + @Override protected void succeeded() { long startEpoch = currentTarget.get().get(0).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); @@ -281,10 +390,11 @@ /* Set tooltip */ /* Java Heap & Metaspace */ - Stream.of(youngUsage, oldUsage, free, metaspaceUsage, metaspaceCapacity) - .flatMap(s -> s.getData().stream()) - .peek(d -> Tooltip.install(d.getNode(), tooltip)) - .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText(String.format("%s: %d MB", epochTimeConverter.toString(d.getXValue()), d.getYValue())))); + IntStream.range(0, currentTarget.get().size()) + .peek(this::setupJavaHeapChartTooltip) + .forEach(this::setupMetaspaceChartTooltip); + + Tooltip tooltip = new Tooltip(); /* Insatances */ instances.getData()
--- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java Wed Dec 07 21:12:08 2016 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java Mon Dec 12 22:04:39 2016 +0900 @@ -54,6 +54,12 @@ */ public class HeapStatsUtils { + /* Rect size in Tooltip for chart legend. */ + public static final double TOOLTIP_LEGEND_RECT_SIZE = 10.0d; + + /* Gap between each controls in Tooltip. */ + public static final double TOOLTIP_GRIDPANE_GAP = 5.0d; + /* Path of HeapStats home directory. */ private static Path currentPath = null;
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/resources.fxml Wed Dec 07 21:12:08 2016 +0900 +++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/resources.fxml Mon Dec 12 22:04:39 2016 +0900 @@ -39,7 +39,7 @@ <children> <StackPane GridPane.columnIndex="1" GridPane.rowIndex="1"> <children> - <LineChart id="threadChart" fx:id="threadChart" animated="false" createSymbols="false" legendVisible="false" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.thread"> + <LineChart id="threadChart" fx:id="threadChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.thread"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis> @@ -61,7 +61,7 @@ </TableView> <StackPane minHeight="0.0" minWidth="0.0"> <children> - <StackedAreaChart id="javaCPUChart" fx:id="javaCPUChart" animated="false" createSymbols="false" legendSide="BOTTOM" legendVisible="true" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.javacpu"> + <StackedAreaChart id="javaCPUChart" fx:id="javaCPUChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.javacpu"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis> @@ -74,7 +74,7 @@ </StackPane> <StackPane minHeight="0.0" minWidth="0.0" GridPane.columnIndex="1"> <children> - <StackedAreaChart id="systemCPUChart" fx:id="systemCPUChart" animated="false" createSymbols="false" legendSide="BOTTOM" legendVisible="true" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.systemcpu"> + <StackedAreaChart id="systemCPUChart" fx:id="systemCPUChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.systemcpu"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis> @@ -87,7 +87,7 @@ </StackPane> <StackPane minHeight="0.0" minWidth="0.0" GridPane.rowIndex="1"> <children> - <LineChart id="javaMemoryChart" fx:id="javaMemoryChart" animated="false" createSymbols="false" legendSide="BOTTOM" legendVisible="true" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.nativememory"> + <LineChart id="javaMemoryChart" fx:id="javaMemoryChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.nativememory"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis> @@ -100,7 +100,7 @@ </StackPane> <StackPane minHeight="0.0" minWidth="0.0" GridPane.rowIndex="2"> <children> - <LineChart id="safepointChart" fx:id="safepointChart" animated="false" createSymbols="false" legendVisible="false" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.safepoint.count"> + <LineChart id="safepointChart" fx:id="safepointChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.safepoint.count"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" visible="false" autoRanging="false" /> </xAxis> @@ -113,7 +113,7 @@ </StackPane> <StackPane minHeight="0.0" minWidth="0.0" GridPane.columnIndex="1" GridPane.rowIndex="2"> <children> - <LineChart id="safepointTimeChart" fx:id="safepointTimeChart" animated="false" createSymbols="false" legendVisible="false" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.safepoint.time"> + <LineChart id="safepointTimeChart" fx:id="safepointTimeChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.safepoint.time"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" visible="false" autoRanging="false" /> </xAxis> @@ -126,7 +126,7 @@ </StackPane> <StackPane minHeight="0.0" minWidth="0.0" GridPane.rowIndex="3"> <children> - <LineChart id="monitorChart" fx:id="monitorChart" animated="false" createSymbols="false" legendVisible="false" minHeight="0.0" minWidth="0.0" onMouseExited="#onChartMouseExited" onMouseMoved="#onChartMouseMoved" title="%chart.monitorcontention"> + <LineChart id="monitorChart" fx:id="monitorChart" animated="false" legendVisible="false" minHeight="0.0" minWidth="0.0" title="%chart.monitorcontention"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis>
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/summary.fxml Wed Dec 07 21:12:08 2016 +0900 +++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/summary.fxml Mon Dec 12 22:04:39 2016 +0900 @@ -44,7 +44,7 @@ <items> <StackPane prefHeight="150.0" prefWidth="200.0"> <children> - <StackedAreaChart id="heapChart" fx:id="heapChart" animated="false" layoutX="-129.0" layoutY="-91.0" minHeight="0.0" title="%chart.javaheap" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <StackedAreaChart id="heapChart" fx:id="heapChart" animated="false" layoutX="-129.0" layoutY="-91.0" minHeight="0.0" title="%chart.javaheap" legendVisible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis> @@ -83,7 +83,7 @@ </StackPane> <StackPane prefHeight="150.0" prefWidth="200.0"> <children> - <AreaChart id="metaspaceChart" fx:id="metaspaceChart" animated="false" layoutX="-174.0" layoutY="-166.0" minHeight="0.0" title="%chart.metaspace" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <AreaChart id="metaspaceChart" fx:id="metaspaceChart" animated="false" layoutX="-174.0" layoutY="-166.0" minHeight="0.0" title="%chart.metaspace" legendVisible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <xAxis> <NumberAxis side="BOTTOM" tickLabelsVisible="false" minorTickVisible="false" autoRanging="false" /> </xAxis>