fetch memory metrics for process
This commit is contained in:
@@ -45,6 +45,12 @@ public interface STS4LanguageClient extends LanguageClient {
|
||||
|
||||
@JsonNotification("sts/liveprocess/updated")
|
||||
void liveProcessDataUpdated(LiveProcessSummary processKey);
|
||||
|
||||
@JsonNotification("sts/liveprocess/memory/metrics/updated")
|
||||
void liveProcessMemoryMetricsDataUpdated(LiveProcessSummary processKey);
|
||||
|
||||
@JsonNotification("sts/liveprocess/gcpauses/metrics/updated")
|
||||
void liveProcessGcPausesMetricsDataUpdated(LiveProcessSummary processKey);
|
||||
|
||||
@JsonNotification("sts/highlight")
|
||||
void highlight(HighlightParams highlights);
|
||||
|
||||
@@ -414,6 +414,15 @@ public class LanguageServerHarness {
|
||||
@Override
|
||||
public void liveProcessDataUpdated(LiveProcessSummary process) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void liveProcessMemoryMetricsDataUpdated(LiveProcessSummary processKey) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void liveProcessGcPausesMetricsDataUpdated(LiveProcessSummary processKey) {
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -31,4 +31,8 @@ public interface ActuatorConnection {
|
||||
String getMetrics(String metric, Map<String, String> tags) throws IOException;
|
||||
|
||||
Map<?, ?> getStartup() throws IOException;
|
||||
|
||||
// String getGcPausesMetrics() throws IOException;
|
||||
|
||||
// String getMemoryMetrics(String metricName) throws IOException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Udayani V
|
||||
*/
|
||||
public class AvailableTags {
|
||||
|
||||
private String tag;
|
||||
private String[] values;
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public String[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AvailableTags [tag=" + tag + ", values=" + Arrays.toString(values) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
/**
|
||||
* @author Udayani V
|
||||
*/
|
||||
public class LiveMemoryMetricsModel {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private Measurements[] measurements;
|
||||
private String baseUnit;
|
||||
private AvailableTags[] availableTags;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Measurements[] getMeasurements() {
|
||||
return measurements;
|
||||
}
|
||||
|
||||
public String getBaseUnit() {
|
||||
return baseUnit;
|
||||
}
|
||||
|
||||
public AvailableTags[] getAvailableTags() {
|
||||
return availableTags;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
public class Measurements {
|
||||
|
||||
private String statistic;
|
||||
private Long value;
|
||||
|
||||
public String getStatistic() {
|
||||
return statistic;
|
||||
}
|
||||
|
||||
|
||||
public Long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Measurements [statistic=" + statistic + ", value=" + value + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,6 +41,8 @@ public class SpringProcessCommandHandler {
|
||||
private static final String COMMAND_DISCONNECT = "sts/livedata/disconnect";
|
||||
private static final String COMMAND_GET = "sts/livedata/get";
|
||||
private static final String COMMAND_LIST_CONNECTED = "sts/livedata/listConnected";
|
||||
private static final String COMMAND_GET_METRICS = "sts/livedata/get/metrics";
|
||||
private static final String COMMAND_GET_REFRESH_METRICS = "sts/livedata/refresh/metrics";
|
||||
|
||||
private final SpringProcessConnectorService connectorService;
|
||||
private final SpringProcessConnectorLocal localProcessConnector;
|
||||
@@ -77,6 +79,16 @@ public class SpringProcessCommandHandler {
|
||||
});
|
||||
log.info("Registered command handler: {}",COMMAND_GET);
|
||||
|
||||
server.onCommand(COMMAND_GET_METRICS, (params) -> {
|
||||
return handleLiveMetricsProcessRequest(params);
|
||||
});
|
||||
log.info("Registered command handler: {}",COMMAND_GET_METRICS);
|
||||
|
||||
server.onCommand(COMMAND_GET_REFRESH_METRICS, (params) -> {
|
||||
return refreshMetrics(params);
|
||||
});
|
||||
log.info("Registered command handler: {}",COMMAND_GET_METRICS);
|
||||
|
||||
server.onCommand(COMMAND_LIST_CONNECTED, (params) -> {
|
||||
List<LiveProcessSummary> result = new ArrayList<>();
|
||||
for (SpringProcessConnector process : connectorService.getConnectedProcesses()) {
|
||||
@@ -136,8 +148,20 @@ public class SpringProcessCommandHandler {
|
||||
|
||||
private CompletableFuture<Object> refresh(ExecuteCommandParams params) {
|
||||
String processKey = getProcessKey(params);
|
||||
String endpoint = getArgumentByKey(params, "endpoint");
|
||||
if (processKey != null) {
|
||||
connectorService.refreshProcess(processKey);
|
||||
connectorService.refreshProcess(processKey, endpoint, "");
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> refreshMetrics(ExecuteCommandParams params) {
|
||||
String processKey = getProcessKey(params);
|
||||
String endpoint = getArgumentByKey(params, "endpoint");
|
||||
String metricName = getArgumentByKey(params, "metricName");
|
||||
if (processKey != null) {
|
||||
connectorService.refreshProcess(processKey, endpoint, metricName);
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
@@ -266,5 +290,25 @@ public class SpringProcessCommandHandler {
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> handleLiveMetricsProcessRequest(ExecuteCommandParams params) {
|
||||
String processKey = getProcessKey(params);
|
||||
String metricName = getArgumentByKey(params, "metricName");
|
||||
if (processKey != null) {
|
||||
switch(metricName) {
|
||||
case "gcPauses": {
|
||||
SpringProcessGcPausesMetricsLiveData data = connectorService.getGcPausesMetricsLiveData(processKey);
|
||||
return CompletableFuture.completedFuture(data.getGcPausesMetrics());
|
||||
}
|
||||
case "memory": {
|
||||
SpringProcessMemoryMetricsLiveData data = connectorService.getMemoryMetricsLiveData(processKey);
|
||||
return CompletableFuture.completedFuture(data.getMemoryMetrics());
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,4 +27,6 @@ public interface SpringProcessConnector {
|
||||
String getProjectName();
|
||||
String getProcessId();
|
||||
String getProcessName();
|
||||
SpringProcessGcPausesMetricsLiveData refreshGcPausesMetrics(SpringProcessLiveData current, String metricName) throws Exception;
|
||||
SpringProcessMemoryMetricsLiveData refreshMemoryMetrics(SpringProcessLiveData current, String metricName) throws Exception;
|
||||
}
|
||||
|
||||
@@ -102,4 +102,18 @@ public class SpringProcessConnectorOverHttp implements SpringProcessConnector {
|
||||
public String toString() {
|
||||
return "SpringProcessConnectorOverHttp [actuatorURL=" + actuatorUrl + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpringProcessGcPausesMetricsLiveData refreshGcPausesMetrics(SpringProcessLiveData current, String metricName)
|
||||
throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpringProcessMemoryMetricsLiveData refreshMemoryMetrics(SpringProcessLiveData current, String metricName)
|
||||
throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -147,6 +147,52 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector {
|
||||
|
||||
throw new Exception("no live data received, lets try again");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpringProcessMemoryMetricsLiveData refreshMemoryMetrics(SpringProcessLiveData currentData, String metricName) throws Exception {
|
||||
log.info("try to open JMX connection to: " + jmxURL);
|
||||
|
||||
if (jmxConnection != null) {
|
||||
try {
|
||||
SpringProcessLiveDataExtractorOverJMX springJMXConnector = new SpringProcessLiveDataExtractorOverJMX();
|
||||
|
||||
log.info("retrieve live data from: " + jmxURL);
|
||||
SpringProcessMemoryMetricsLiveData liveData = springJMXConnector.retrieveLiveMemoryMetricsData(getProcessType(), jmxConnection, processID, processName, currentData, metricName);
|
||||
|
||||
if (liveData != null && liveData.getMemoryMetrics() != null && liveData.getMemoryMetrics().length > 0) {
|
||||
return liveData;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("exception while connecting to jmx: " + jmxURL, e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("no live data received, lets try again");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpringProcessGcPausesMetricsLiveData refreshGcPausesMetrics(SpringProcessLiveData currentData, String metricName) throws Exception {
|
||||
log.info("try to open JMX connection to: " + jmxURL);
|
||||
|
||||
if (jmxConnection != null) {
|
||||
try {
|
||||
SpringProcessLiveDataExtractorOverJMX springJMXConnector = new SpringProcessLiveDataExtractorOverJMX();
|
||||
|
||||
log.info("retrieve live data from: " + jmxURL);
|
||||
SpringProcessGcPausesMetricsLiveData liveData = springJMXConnector.retrieveLiveGcPausesMetricsData(getProcessType(), jmxConnection, processID, processName, currentData, metricName);
|
||||
|
||||
if (liveData != null && liveData.getGcPausesMetrics() != null && liveData.getGcPausesMetrics().length > 0) {
|
||||
return liveData;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("exception while connecting to jmx: " + jmxURL, e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("no live data received, lets try again");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() throws Exception {
|
||||
|
||||
@@ -99,7 +99,7 @@ public class SpringProcessConnectorService {
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshProcess(String processKey) {
|
||||
public void refreshProcess(String processKey, String endpoint, String metricName) {
|
||||
log.info("refresh process: " + processKey);
|
||||
|
||||
SpringProcessConnector connector = this.connectors.get(processKey);
|
||||
@@ -109,13 +109,21 @@ public class SpringProcessConnectorService {
|
||||
|
||||
progressTask.progressBegin("Refresh", null);
|
||||
|
||||
scheduleRefresh(progressTask, processKey, connector, 0, TimeUnit.SECONDS, 0);
|
||||
scheduleRefresh(progressTask, processKey, connector, 0, TimeUnit.SECONDS, 0, endpoint, metricName);
|
||||
}
|
||||
}
|
||||
|
||||
public SpringProcessLiveData getLiveData(String processKey) {
|
||||
return this.liveDataProvider.getCurrent(processKey);
|
||||
}
|
||||
|
||||
public SpringProcessMemoryMetricsLiveData getMemoryMetricsLiveData(String processKey) {
|
||||
return this.liveDataProvider.getMemoryMetrics(processKey);
|
||||
}
|
||||
|
||||
public SpringProcessGcPausesMetricsLiveData getGcPausesMetricsLiveData(String processKey) {
|
||||
return this.liveDataProvider.getGcPausesMetrics(processKey);
|
||||
}
|
||||
|
||||
public void disconnectProcess(String processKey) {
|
||||
log.info("disconnect from process: " + processKey);
|
||||
@@ -162,7 +170,9 @@ public class SpringProcessConnectorService {
|
||||
connector.connect();
|
||||
progressTask.progressDone();
|
||||
|
||||
refreshProcess(processKey);
|
||||
refreshProcess(processKey, "", "");
|
||||
refreshProcess(processKey, "metrics", "memory");
|
||||
refreshProcess(processKey, "metrics", "gcPauses");
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.info("problem occured during process connect", e);
|
||||
@@ -210,7 +220,7 @@ public class SpringProcessConnectorService {
|
||||
}, delay, unit);
|
||||
}
|
||||
|
||||
private void scheduleRefresh(ProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
|
||||
private void scheduleRefresh(ProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo, String endpoint, String metricName) {
|
||||
String progressMessage = "Refreshing data from Spring process: " + processKey + " - retry no: " + retryNo;
|
||||
log.info(progressMessage);
|
||||
|
||||
@@ -219,14 +229,38 @@ public class SpringProcessConnectorService {
|
||||
|
||||
try {
|
||||
progressTask.progressEvent(progressMessage);
|
||||
SpringProcessLiveData newLiveData = connector.refresh(this.liveDataProvider.getCurrent(processKey));
|
||||
|
||||
if (newLiveData != null) {
|
||||
if (!this.liveDataProvider.add(processKey, newLiveData)) {
|
||||
this.liveDataProvider.update(processKey, newLiveData);
|
||||
if(endpoint.equals("metrics") && metricName.equals("memory")) {
|
||||
SpringProcessMemoryMetricsLiveData newMetricsLiveData = connector.refreshMemoryMetrics(this.liveDataProvider.getCurrent(processKey), metricName);
|
||||
|
||||
if (newMetricsLiveData != null) {
|
||||
if (!this.liveDataProvider.addMemoryMetrics(processKey, newMetricsLiveData)) {
|
||||
this.liveDataProvider.updateMemoryMetrics(processKey, newMetricsLiveData);
|
||||
}
|
||||
|
||||
this.connectedSuccess.put(processKey, true);
|
||||
}
|
||||
|
||||
this.connectedSuccess.put(processKey, true);
|
||||
} else if(endpoint.equals("metrics") && metricName.equals("gcPauses")) {
|
||||
SpringProcessGcPausesMetricsLiveData newMetricsLiveData = connector.refreshGcPausesMetrics(this.liveDataProvider.getCurrent(processKey), metricName);
|
||||
|
||||
if (newMetricsLiveData != null) {
|
||||
if (!this.liveDataProvider.addGcPausesMetrics(processKey, newMetricsLiveData)) {
|
||||
this.liveDataProvider.updateGcPausesMetrics(processKey, newMetricsLiveData);
|
||||
}
|
||||
|
||||
this.connectedSuccess.put(processKey, true);
|
||||
}
|
||||
|
||||
} else {
|
||||
SpringProcessLiveData newLiveData = connector.refresh(this.liveDataProvider.getCurrent(processKey));
|
||||
|
||||
if (newLiveData != null) {
|
||||
if (!this.liveDataProvider.add(processKey, newLiveData)) {
|
||||
this.liveDataProvider.update(processKey, newLiveData);
|
||||
}
|
||||
|
||||
this.connectedSuccess.put(processKey, true);
|
||||
}
|
||||
}
|
||||
progressTask.progressDone();
|
||||
}
|
||||
@@ -236,7 +270,7 @@ public class SpringProcessConnectorService {
|
||||
|
||||
if (retryNo < maxRetryCount && isKnownProcessKey(processKey)) {
|
||||
scheduleRefresh(progressTask, processKey, connector, retryDelayInSeconds, TimeUnit.SECONDS,
|
||||
retryNo + 1);
|
||||
retryNo + 1, endpoint, metricName);
|
||||
}
|
||||
else {
|
||||
progressTask.progressDone();
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019, 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
/**
|
||||
* @author V Udayani
|
||||
*/
|
||||
public class SpringProcessGcPausesMetricsLiveData {
|
||||
|
||||
private final ProcessType processType;
|
||||
private final String processName;
|
||||
private final String processID;
|
||||
|
||||
private final LiveMemoryMetricsModel[] gcPausesMetrics;
|
||||
|
||||
public SpringProcessGcPausesMetricsLiveData(ProcessType processType, String processName, String processID, LiveMemoryMetricsModel[] gcPausesMetrics) {
|
||||
super();
|
||||
this.processType = processType;
|
||||
this.processName = processName;
|
||||
this.processID = processID;
|
||||
this.gcPausesMetrics = gcPausesMetrics;
|
||||
|
||||
}
|
||||
|
||||
public ProcessType getProcessType() {
|
||||
return processType;
|
||||
}
|
||||
|
||||
public String getProcessName() {
|
||||
return this.processName;
|
||||
}
|
||||
|
||||
public String getProcessID() {
|
||||
return this.processID;
|
||||
}
|
||||
|
||||
public LiveMemoryMetricsModel[] getGcPausesMetrics() {
|
||||
return this.gcPausesMetrics;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -76,6 +76,8 @@ public class SpringProcessLiveDataExtractorOverHttp {
|
||||
LiveBeansModel beans = getBeans(connection);
|
||||
LiveMetricsModel metrics = getMetrics(connection);
|
||||
StartupMetricsModel startup = getStartupMetrics(connection, currentData == null ? null : currentData.getStartupMetrics());
|
||||
// LiveMemoryMetricsModel[] memoryMetrics = getMemoryMetrics(connection);
|
||||
// LiveMemoryMetricsModel gcPausesMetrics = getGcPausesMetrics(connection);
|
||||
|
||||
if (contextPath == null) {
|
||||
contextPath = getContextPath(environment);
|
||||
@@ -99,7 +101,10 @@ public class SpringProcessLiveDataExtractorOverHttp {
|
||||
conditionals,
|
||||
properties,
|
||||
metrics,
|
||||
startup);
|
||||
startup
|
||||
);
|
||||
// memoryMetrics,
|
||||
// gcPausesMetrics);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error reading live data from: " + processID + " - " + processName, e);
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -36,6 +37,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -95,6 +97,8 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
||||
LiveBeansModel beans = getBeans(connection, domain);
|
||||
LiveMetricsModel metrics = getMetrics(connection, domain);
|
||||
StartupMetricsModel startup = getStartupMetrics(connection, domain, currentData == null ? null : currentData.getStartupMetrics());
|
||||
// LiveMemoryMetricsModel[] memoryMetrics = getLiveMemoryMetrics(connection, domain);
|
||||
// LiveMemoryMetricsModel gcPausesMetrics = getGcPausesMetrics(connection, domain);
|
||||
|
||||
if (contextPath == null) {
|
||||
contextPath = getContextPath(connection, domain, environment);
|
||||
@@ -118,7 +122,10 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
||||
conditionals,
|
||||
properties,
|
||||
metrics,
|
||||
startup);
|
||||
startup
|
||||
// memoryMetrics,
|
||||
// gcPausesMetrics
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error reading live data from: " + processID + " - " + processName, e);
|
||||
@@ -127,6 +134,108 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param processType
|
||||
* @param processID if null, will be determined searching existing mbeans for that information (for remote processes via platform beans runtime name)
|
||||
* @param processName if null, will be determined searching existing mbeans for that information (for remote processes inferring the java command from the system properties)
|
||||
* @param currentData currently stored live data
|
||||
* @param metricName
|
||||
*/
|
||||
public SpringProcessMemoryMetricsLiveData retrieveLiveMemoryMetricsData(ProcessType processType, JMXConnector jmxConnector, String processID, String processName,
|
||||
SpringProcessLiveData currentData, String metricName) {
|
||||
|
||||
List<String> memoryTags = Arrays.asList( "jvm.memory.used", "jvm.memory.committed", "jvm.memory.max");
|
||||
|
||||
try {
|
||||
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
|
||||
List<LiveMemoryMetricsModel> memoryMetricsList = new ArrayList<>();
|
||||
String domain = getDomainForActuator(connection);
|
||||
|
||||
if (processID == null) {
|
||||
processID = getProcessID(connection);
|
||||
}
|
||||
|
||||
if (processName == null) {
|
||||
Properties systemProperties = getSystemProperties(connection);
|
||||
if (systemProperties != null) {
|
||||
String javaCommand = getJavaCommand(systemProperties);
|
||||
processName = getProcessName(javaCommand);
|
||||
}
|
||||
}
|
||||
|
||||
// if(metricName.equals("memory")) {
|
||||
for(String metric : memoryTags) {
|
||||
LiveMemoryMetricsModel metrics = getLiveMetrics(connection, domain, metric);
|
||||
if(metrics != null) {
|
||||
memoryMetricsList.add(metrics);
|
||||
}
|
||||
}
|
||||
|
||||
// }
|
||||
|
||||
LiveMemoryMetricsModel[] res = (LiveMemoryMetricsModel[]) memoryMetricsList.toArray(new LiveMemoryMetricsModel[memoryMetricsList.size()]);
|
||||
return new SpringProcessMemoryMetricsLiveData(
|
||||
processType,
|
||||
processName,
|
||||
processID,
|
||||
res
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error reading live metrics data from: " + processID + " - " + processName, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param processType
|
||||
* @param processID if null, will be determined searching existing mbeans for that information (for remote processes via platform beans runtime name)
|
||||
* @param processName if null, will be determined searching existing mbeans for that information (for remote processes inferring the java command from the system properties)
|
||||
* @param currentData currently stored live data
|
||||
* @param metricName
|
||||
*/
|
||||
public SpringProcessGcPausesMetricsLiveData retrieveLiveGcPausesMetricsData(ProcessType processType, JMXConnector jmxConnector, String processID, String processName,
|
||||
SpringProcessLiveData currentData, String metricName) {
|
||||
|
||||
try {
|
||||
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
|
||||
List<LiveMemoryMetricsModel> memoryMetricsList = new ArrayList<>();
|
||||
String domain = getDomainForActuator(connection);
|
||||
|
||||
if (processID == null) {
|
||||
processID = getProcessID(connection);
|
||||
}
|
||||
|
||||
if (processName == null) {
|
||||
Properties systemProperties = getSystemProperties(connection);
|
||||
if (systemProperties != null) {
|
||||
String javaCommand = getJavaCommand(systemProperties);
|
||||
processName = getProcessName(javaCommand);
|
||||
}
|
||||
}
|
||||
|
||||
LiveMemoryMetricsModel metrics = getLiveMetrics(connection, domain, "jvm.gc.pause");
|
||||
if(metrics != null) {
|
||||
memoryMetricsList.add(getLiveMetrics(connection, domain, "jvm.gc.pause"));
|
||||
}
|
||||
|
||||
|
||||
LiveMemoryMetricsModel[] res = (LiveMemoryMetricsModel[]) memoryMetricsList.toArray(new LiveMemoryMetricsModel[memoryMetricsList.size()]);
|
||||
return new SpringProcessGcPausesMetricsLiveData(
|
||||
processType,
|
||||
processName,
|
||||
processID,
|
||||
res
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error reading live metrics data from: " + processID + " - " + processName, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private LiveMetricsModel getMetrics(MBeanServerConnection connection, String domain) {
|
||||
|
||||
return new LiveMetricsModel() {
|
||||
@@ -257,6 +366,31 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
||||
}
|
||||
return LiveBeansModel.builder().build();
|
||||
}
|
||||
|
||||
|
||||
public LiveMemoryMetricsModel getLiveMetrics(MBeanServerConnection connection, String domain, String metricName) {
|
||||
|
||||
List<Object> tags = new ArrayList<>();
|
||||
Object[] params1 = new Object[] {metricName, tags};
|
||||
String[] signature = new String[] {String.class.getName(), List.class.getName()};
|
||||
|
||||
try {
|
||||
Object metricsData = getActuatorDataFromOperation(connection,
|
||||
getObjectName(domain, "type=Endpoint,name=Metrics"),
|
||||
"metric",
|
||||
params1,
|
||||
signature);
|
||||
if (metricsData instanceof String) {
|
||||
return gson.fromJson((String)metricsData, LiveMemoryMetricsModel.class);
|
||||
} else if(metricsData != null){
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.convertValue(metricsData, LiveMemoryMetricsModel.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object getBeansFromNonBootMBean(MBeanServerConnection connection) throws Exception {
|
||||
Set<ObjectName> nonBootSpringLiveMBeans = getNonBootSpringLiveMBeans(connection);
|
||||
|
||||
@@ -27,12 +27,16 @@ import org.springframework.ide.vscode.commons.util.Assert;
|
||||
public class SpringProcessLiveDataProvider {
|
||||
|
||||
private final ConcurrentMap<String, SpringProcessLiveData> liveData;
|
||||
private final ConcurrentMap<String, SpringProcessMemoryMetricsLiveData> memoryMetricsLiveData;
|
||||
private final ConcurrentMap<String, SpringProcessGcPausesMetricsLiveData> gcPausesMetricsLiveData;
|
||||
private final List<SpringProcessLiveDataChangeListener> listeners;
|
||||
private final SimpleLanguageServer server;
|
||||
|
||||
public SpringProcessLiveDataProvider(SimpleLanguageServer server) {
|
||||
this.server = server;
|
||||
this.liveData = new ConcurrentHashMap<>();
|
||||
this.memoryMetricsLiveData = new ConcurrentHashMap<>();
|
||||
this.gcPausesMetricsLiveData = new ConcurrentHashMap<>();
|
||||
this.listeners = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
@@ -54,6 +58,22 @@ public class SpringProcessLiveDataProvider {
|
||||
return oldData == null;
|
||||
}
|
||||
|
||||
public boolean addMemoryMetrics(String processKey, SpringProcessMemoryMetricsLiveData liveData) {
|
||||
SpringProcessMemoryMetricsLiveData oldData = this.memoryMetricsLiveData.putIfAbsent(processKey, liveData);
|
||||
if (oldData == null) {
|
||||
getClient().liveProcessMemoryMetricsDataUpdated(createProcessSummaryForMetrics(processKey, liveData.getProcessType().jsonName(),liveData.getProcessName(),liveData.getProcessID()));
|
||||
}
|
||||
return oldData == null;
|
||||
}
|
||||
|
||||
public boolean addGcPausesMetrics(String processKey, SpringProcessGcPausesMetricsLiveData liveData) {
|
||||
SpringProcessGcPausesMetricsLiveData oldData = this.gcPausesMetricsLiveData.putIfAbsent(processKey, liveData);
|
||||
if (oldData == null) {
|
||||
getClient().liveProcessGcPausesMetricsDataUpdated(createProcessSummaryForMetrics(processKey, liveData.getProcessType().jsonName(),liveData.getProcessName(),liveData.getProcessID()));
|
||||
}
|
||||
return oldData == null;
|
||||
}
|
||||
|
||||
private STS4LanguageClient getClient() {
|
||||
STS4LanguageClient client = server.getClient();
|
||||
Assert.isLegal(client!=null, "Client is null. Language server not yet initialized?");
|
||||
@@ -74,6 +94,16 @@ public class SpringProcessLiveDataProvider {
|
||||
getClient().liveProcessDataUpdated(createProcessSummary(processKey, liveData));
|
||||
}
|
||||
|
||||
public void updateMemoryMetrics(String processKey, SpringProcessMemoryMetricsLiveData liveData) {
|
||||
this.memoryMetricsLiveData.put(processKey, liveData);
|
||||
getClient().liveProcessMemoryMetricsDataUpdated(createProcessSummaryForMetrics(processKey, liveData.getProcessType().jsonName(),liveData.getProcessName(),liveData.getProcessID()));
|
||||
}
|
||||
|
||||
public void updateGcPausesMetrics(String processKey, SpringProcessGcPausesMetricsLiveData liveData) {
|
||||
this.gcPausesMetricsLiveData.put(processKey, liveData);
|
||||
getClient().liveProcessGcPausesMetricsDataUpdated(createProcessSummaryForMetrics(processKey, liveData.getProcessType().jsonName(),liveData.getProcessName(),liveData.getProcessID()));
|
||||
}
|
||||
|
||||
public void addLiveDataChangeListener(SpringProcessLiveDataChangeListener listener) {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
@@ -93,6 +123,14 @@ public class SpringProcessLiveDataProvider {
|
||||
public SpringProcessLiveData getCurrent(String processKey) {
|
||||
return this.liveData.get(processKey);
|
||||
}
|
||||
|
||||
public SpringProcessMemoryMetricsLiveData getMemoryMetrics(String processKey) {
|
||||
return this.memoryMetricsLiveData.get(processKey);
|
||||
}
|
||||
|
||||
public SpringProcessGcPausesMetricsLiveData getGcPausesMetrics(String processKey) {
|
||||
return this.gcPausesMetricsLiveData.get(processKey);
|
||||
}
|
||||
|
||||
public static LiveProcessSummary createProcessSummary(String processKey, SpringProcessLiveData liveData) {
|
||||
LiveProcessSummary p = new LiveProcessSummary();
|
||||
@@ -102,5 +140,15 @@ public class SpringProcessLiveDataProvider {
|
||||
p.setPid(liveData.getProcessID());
|
||||
return p;
|
||||
}
|
||||
|
||||
public static LiveProcessSummary createProcessSummaryForMetrics(String processKey, String processType, String processName,
|
||||
String processID) {
|
||||
LiveProcessSummary p = new LiveProcessSummary();
|
||||
p.setType(processType);
|
||||
p.setProcessKey(processKey);
|
||||
p.setProcessName(processName);
|
||||
p.setPid(processID);
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019, 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
/**
|
||||
* @author V Udayani
|
||||
*/
|
||||
public class SpringProcessMemoryMetricsLiveData {
|
||||
|
||||
private final ProcessType processType;
|
||||
private final String processName;
|
||||
private final String processID;
|
||||
|
||||
private final LiveMemoryMetricsModel[] memoryMetrics;
|
||||
|
||||
public SpringProcessMemoryMetricsLiveData(ProcessType processType, String processName, String processID, LiveMemoryMetricsModel[] memoryMetrics) {
|
||||
super();
|
||||
this.processType = processType;
|
||||
this.processName = processName;
|
||||
this.processID = processID;
|
||||
this.memoryMetrics = memoryMetrics;
|
||||
|
||||
}
|
||||
|
||||
public ProcessType getProcessType() {
|
||||
return processType;
|
||||
}
|
||||
|
||||
public String getProcessName() {
|
||||
return this.processName;
|
||||
}
|
||||
|
||||
public String getProcessID() {
|
||||
return this.processID;
|
||||
}
|
||||
|
||||
public LiveMemoryMetricsModel[] getMemoryMetrics() {
|
||||
return this.memoryMetrics;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,10 +20,35 @@ export interface ExtensionAPI {
|
||||
*/
|
||||
readonly onDidLiveProcessUpdate: Event<LiveProcess>
|
||||
|
||||
/**
|
||||
* An event which fires on live process gcpauses metrics data change. Payload is processKey.
|
||||
*/
|
||||
readonly onDidLiveProcessGcPausesMetricsUpdate: Event<LiveProcess>
|
||||
|
||||
/**
|
||||
* An event which fires on live process memory metrics data change. Payload is processKey.
|
||||
*/
|
||||
readonly onDidLiveProcessMemoryMetricsUpdate: Event<LiveProcess>
|
||||
|
||||
/**
|
||||
* A command to get live process data.
|
||||
*/
|
||||
readonly getLiveProcessData: (query: SimpleQuery | BeansQuery) => Promise<any>
|
||||
readonly getLiveProcessData: (query: SimpleQuery | BeansQuery ) => Promise<any>
|
||||
|
||||
/**
|
||||
* A command to refresh live process data.
|
||||
*/
|
||||
readonly refreshLiveProcessData: (query: SimpleQuery | BeansQuery) => Promise<any>;
|
||||
|
||||
/**
|
||||
* A command to get live process metrics data.
|
||||
*/
|
||||
readonly getLiveProcessMetricsData: (query: MetricsQuery) => Promise<any>;
|
||||
|
||||
/**
|
||||
* A command to refresh live process metrics data.
|
||||
*/
|
||||
readonly refreshLiveProcessMetricsData: (query: MetricsQuery) => Promise<any>;
|
||||
|
||||
/**
|
||||
* A command to list all currently connected processes.
|
||||
@@ -52,3 +77,10 @@ interface BeansQuery extends LiveProcessDataQuery {
|
||||
beanName?: string;
|
||||
dependingOn?: string;
|
||||
}
|
||||
|
||||
interface MetricsQuery extends LiveProcessDataQuery {
|
||||
endpoint: "metrics";
|
||||
metricName: string;
|
||||
tag?: { key: string; value: string };
|
||||
|
||||
}
|
||||
|
||||
@@ -1,39 +1,68 @@
|
||||
import { commands, Uri } from "vscode";
|
||||
import { Emitter, LanguageClient } from "vscode-languageclient/node";
|
||||
import { ExtensionAPI } from "./api";
|
||||
import { LiveProcess, LiveProcessConnectedNotification, LiveProcessDisconnectedNotification, LiveProcessUpdatedNotification } from "./notification";
|
||||
import { LiveProcess, LiveProcessConnectedNotification, LiveProcessDisconnectedNotification, LiveProcessUpdatedNotification, LiveProcessGcPausesMetricsUpdatedNotification, LiveProcessMemoryMetricsUpdatedNotification } from "./notification";
|
||||
|
||||
export class ApiManager {
|
||||
public api: ExtensionAPI;
|
||||
private onDidLiveProcessConnectEmitter: Emitter<LiveProcess> = new Emitter<LiveProcess>();
|
||||
private onDidLiveProcessDisconnectEmitter: Emitter<LiveProcess> = new Emitter<LiveProcess>();
|
||||
private onDidLiveProcessUpdateEmitter: Emitter<LiveProcess> = new Emitter<LiveProcess>();
|
||||
private onDidLiveProcessGcPausesMetricsUpdateEmitter: Emitter<LiveProcess> = new Emitter<LiveProcess>();
|
||||
private onDidLiveProcessMemoryMetricsUpdateEmitter: Emitter<LiveProcess> = new Emitter<LiveProcess>();
|
||||
|
||||
public constructor(client: LanguageClient) {
|
||||
const onDidLiveProcessConnect = this.onDidLiveProcessConnectEmitter.event;
|
||||
const onDidLiveProcessDisconnect = this.onDidLiveProcessDisconnectEmitter.event;
|
||||
const onDidLiveProcessUpdate = this.onDidLiveProcessUpdateEmitter.event;
|
||||
const onDidLiveProcessGcPausesMetricsUpdate = this.onDidLiveProcessGcPausesMetricsUpdateEmitter.event;
|
||||
const onDidLiveProcessMemoryMetricsUpdate = this.onDidLiveProcessMemoryMetricsUpdateEmitter.event;
|
||||
|
||||
const COMMAND_LIVEDATA_GET = "sts/livedata/get";
|
||||
const getLiveProcessData = async (query) => {
|
||||
return await commands.executeCommand(COMMAND_LIVEDATA_GET, query);
|
||||
}
|
||||
|
||||
const COMMAND_LIVEDATA_REFRESH = "sts/livedata/refresh";
|
||||
const refreshLiveProcessData = async (query) => {
|
||||
return await commands.executeCommand(COMMAND_LIVEDATA_REFRESH, query);
|
||||
}
|
||||
|
||||
const COMMAND_LIVEDATA_LIST_CONNECTED = "sts/livedata/listConnected"
|
||||
const listConnectedProcesses = async () : Promise<LiveProcess[]> => {
|
||||
return await commands.executeCommand(COMMAND_LIVEDATA_LIST_CONNECTED);
|
||||
}
|
||||
|
||||
const COMMAND_LIVEDATA_GET_METRICS = "sts/livedata/get/metrics"
|
||||
const getLiveProcessMetricsData = async (query) : Promise<LiveProcess[]> => {
|
||||
console.log(query);
|
||||
console.log("in get live metrics function");
|
||||
return await commands.executeCommand(COMMAND_LIVEDATA_GET_METRICS, query);
|
||||
}
|
||||
|
||||
const COMMAND_LIVEDATA_REFRESH_METRICS = "sts/livedata/refresh/metrics";
|
||||
const refreshLiveProcessMetricsData = async (query) => {
|
||||
console.log("in get live metrics refresh function");
|
||||
return await commands.executeCommand(COMMAND_LIVEDATA_REFRESH_METRICS, query);
|
||||
}
|
||||
|
||||
client.onNotification(LiveProcessConnectedNotification.type, (process: LiveProcess) => this.onDidLiveProcessConnectEmitter.fire(process));
|
||||
client.onNotification(LiveProcessDisconnectedNotification.type, (process: LiveProcess) => this.onDidLiveProcessDisconnectEmitter.fire(process));
|
||||
client.onNotification(LiveProcessUpdatedNotification.type, (process: LiveProcess) => this.onDidLiveProcessUpdateEmitter.fire(process));
|
||||
client.onNotification(LiveProcessGcPausesMetricsUpdatedNotification.type, (process: LiveProcess) => this.onDidLiveProcessGcPausesMetricsUpdateEmitter.fire(process));
|
||||
client.onNotification(LiveProcessMemoryMetricsUpdatedNotification.type, (process: LiveProcess) => this.onDidLiveProcessMemoryMetricsUpdateEmitter.fire(process));
|
||||
|
||||
this.api = {
|
||||
client,
|
||||
onDidLiveProcessConnect,
|
||||
onDidLiveProcessDisconnect,
|
||||
onDidLiveProcessUpdate,
|
||||
onDidLiveProcessMemoryMetricsUpdate,
|
||||
onDidLiveProcessGcPausesMetricsUpdate,
|
||||
getLiveProcessData,
|
||||
refreshLiveProcessData,
|
||||
getLiveProcessMetricsData,
|
||||
refreshLiveProcessMetricsData,
|
||||
listConnectedProcesses,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,4 +28,12 @@ export namespace LiveProcessDisconnectedNotification {
|
||||
|
||||
export namespace LiveProcessUpdatedNotification {
|
||||
export const type = new NotificationType<LiveProcess>('sts/liveprocess/updated');
|
||||
}
|
||||
|
||||
export namespace LiveProcessGcPausesMetricsUpdatedNotification {
|
||||
export const type = new NotificationType<LiveProcess>('sts/liveprocess/gcpauses/metrics/updated');
|
||||
}
|
||||
|
||||
export namespace LiveProcessMemoryMetricsUpdatedNotification {
|
||||
export const type = new NotificationType<LiveProcess>('sts/liveprocess/memory/metrics/updated');
|
||||
}
|
||||
Reference in New Issue
Block a user