Startup metrics initial work (before unit test)
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.springframework.ide.vscode.boot.java.livehover;
|
package org.springframework.ide.vscode.boot.java.livehover;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -185,6 +186,16 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
|
|||||||
if (!wiredBeans.isEmpty()) {
|
if (!wiredBeans.isEmpty()) {
|
||||||
AutowiredHoverProvider.createHoverContentForBeans(sourceLinks, project, hover, wiredBeans);
|
AutowiredHoverProvider.createHoverContentForBeans(sourceLinks, project, hover, wiredBeans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (liveData.getStartup() != null) {
|
||||||
|
Duration instanciationTime = liveData.getStartup().getBeanInstanciationTime(definedBean.getId());
|
||||||
|
if (instanciationTime != null) {
|
||||||
|
hover.append("Instanciation Time: ");
|
||||||
|
hover.append(instanciationTime.toMillis());
|
||||||
|
hover.append("ms");
|
||||||
|
hover.append("\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hover.append("Bean id: `");
|
hover.append("Bean id: `");
|
||||||
hover.append(definedBean.getId());
|
hover.append(definedBean.getId());
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public interface SpringProcessConnector {
|
|||||||
String getProcessKey();
|
String getProcessKey();
|
||||||
|
|
||||||
void connect() throws Exception;
|
void connect() throws Exception;
|
||||||
SpringProcessLiveData refresh() throws Exception;
|
SpringProcessLiveData refresh(SpringProcessLiveData currentData) throws Exception;
|
||||||
void disconnect() throws Exception;
|
void disconnect() throws Exception;
|
||||||
|
|
||||||
void addConnectorChangeListener(SpringProcessConnectionChangeListener listener);
|
void addConnectorChangeListener(SpringProcessConnectionChangeListener listener);
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpringProcessLiveData refresh() throws Exception {
|
public SpringProcessLiveData refresh(SpringProcessLiveData currentData) throws Exception {
|
||||||
log.info("try to open JMX connection to: " + jmxURL);
|
log.info("try to open JMX connection to: " + jmxURL);
|
||||||
|
|
||||||
if (jmxConnection != null) {
|
if (jmxConnection != null) {
|
||||||
@@ -119,7 +119,7 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info("retrieve live data from: " + jmxURL);
|
log.info("retrieve live data from: " + jmxURL);
|
||||||
SpringProcessLiveData liveData = springJMXConnector.retrieveLiveData(jmxConnection, processID, processName, urlScheme, host, null, port);
|
SpringProcessLiveData liveData = springJMXConnector.retrieveLiveData(jmxConnection, processID, processName, urlScheme, host, null, port, currentData);
|
||||||
|
|
||||||
if (this.processID == null) {
|
if (this.processID == null) {
|
||||||
this.processID = liveData.getProcessID();
|
this.processID = liveData.getProcessID();
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ public class SpringProcessConnectorService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
progressTask.progressEvent(progressMessage);
|
progressTask.progressEvent(progressMessage);
|
||||||
SpringProcessLiveData newLiveData = connector.refresh();
|
SpringProcessLiveData newLiveData = connector.refresh(this.liveDataProvider.getCurrent(processKey));
|
||||||
|
|
||||||
if (newLiveData != null) {
|
if (newLiveData != null) {
|
||||||
if (!this.liveDataProvider.add(processKey, newLiveData)) {
|
if (!this.liveDataProvider.add(processKey, newLiveData)) {
|
||||||
|
|||||||
@@ -29,11 +29,12 @@ public class SpringProcessLiveData {
|
|||||||
private final LiveConditional[] conditionals;
|
private final LiveConditional[] conditionals;
|
||||||
private final LiveProperties properties;
|
private final LiveProperties properties;
|
||||||
private final LiveMetricsModel metrics;
|
private final LiveMetricsModel metrics;
|
||||||
|
private StartupModel startup;
|
||||||
|
|
||||||
public SpringProcessLiveData(String processName, String processID, String contextPath, String urlScheme,
|
public SpringProcessLiveData(String processName, String processID, String contextPath, String urlScheme,
|
||||||
String port, String host, LiveBeansModel beansModel, String[] activeProfiles,
|
String port, String host, LiveBeansModel beansModel, String[] activeProfiles,
|
||||||
LiveRequestMapping[] requestMappings, LiveConditional[] conditionals, LiveProperties properties,
|
LiveRequestMapping[] requestMappings, LiveConditional[] conditionals, LiveProperties properties,
|
||||||
LiveMetricsModel metrics) {
|
LiveMetricsModel metrics, StartupModel startup) {
|
||||||
super();
|
super();
|
||||||
this.processName = processName;
|
this.processName = processName;
|
||||||
this.processID = processID;
|
this.processID = processID;
|
||||||
@@ -47,6 +48,8 @@ public class SpringProcessLiveData {
|
|||||||
this.conditionals = conditionals;
|
this.conditionals = conditionals;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.metrics = metrics;
|
this.metrics = metrics;
|
||||||
|
this.startup = startup;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProcessName() {
|
public String getProcessName() {
|
||||||
@@ -97,4 +100,8 @@ public class SpringProcessLiveData {
|
|||||||
return this.metrics;
|
return this.metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StartupModel getStartup() {
|
||||||
|
return startup;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -61,9 +62,10 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
|||||||
* @param host should always be != null
|
* @param host should always be != null
|
||||||
* @param contextPath if null, will be determined searching existing mbeans for that information (for local processes)
|
* @param contextPath if null, will be determined searching existing mbeans for that information (for local processes)
|
||||||
* @param port if null, will be determined searching existing mbeans for that information (for local processes)
|
* @param port if null, will be determined searching existing mbeans for that information (for local processes)
|
||||||
|
* @param currentData currently stored live data
|
||||||
*/
|
*/
|
||||||
public SpringProcessLiveData retrieveLiveData(JMXConnector jmxConnector, String processID, String processName,
|
public SpringProcessLiveData retrieveLiveData(JMXConnector jmxConnector, String processID, String processName,
|
||||||
String urlScheme, String host, String contextPath, String port) {
|
String urlScheme, String host, String contextPath, String port, SpringProcessLiveData currentData) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
|
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
|
||||||
@@ -91,6 +93,7 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
|||||||
LiveRequestMapping[] requestMappings = getRequestMappings(connection, domain);
|
LiveRequestMapping[] requestMappings = getRequestMappings(connection, domain);
|
||||||
LiveBeansModel beans = getBeans(connection, domain);
|
LiveBeansModel beans = getBeans(connection, domain);
|
||||||
LiveMetricsModel metrics = getMetrics(connection, domain);
|
LiveMetricsModel metrics = getMetrics(connection, domain);
|
||||||
|
StartupModel startup = getStartup(connection, domain, currentData == null ? null : currentData.getStartup());
|
||||||
|
|
||||||
if (contextPath == null) {
|
if (contextPath == null) {
|
||||||
contextPath = getContextPath(connection, domain, environment);
|
contextPath = getContextPath(connection, domain, environment);
|
||||||
@@ -112,7 +115,8 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
|||||||
requestMappings,
|
requestMappings,
|
||||||
conditionals,
|
conditionals,
|
||||||
properties,
|
properties,
|
||||||
metrics);
|
metrics,
|
||||||
|
startup);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.error("error reading live data from: " + processID + " - " + processName, e);
|
log.error("error reading live data from: " + processID + " - " + processName, e);
|
||||||
@@ -159,6 +163,21 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
|||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StartupModel getStartup(MBeanServerConnection connection, String domain, StartupModel currentStartup) {
|
||||||
|
if (currentStartup != null) {
|
||||||
|
return currentStartup;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Map<?,?> result = (Map<?,?>) getActuatorDataFromOperation(connection, getObjectName(domain, "type=Endpoint,name=Startup"), "startup");
|
||||||
|
if (result != null) {
|
||||||
|
return StartupModel.parse(result);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public String getProcessID(MBeanServerConnection connection) {
|
public String getProcessID(MBeanServerConnection connection) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -78,5 +78,9 @@ public class SpringProcessLiveDataProvider {
|
|||||||
listener.liveDataChanged(event);
|
listener.liveDataChanged(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpringProcessLiveData getCurrent(String processKey) {
|
||||||
|
return this.liveData.get(processKey);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
public class StartupModel {
|
||||||
|
|
||||||
|
private static final String BEAN_INSTANCIATION_EVENT = "spring.beans.instantiate";
|
||||||
|
|
||||||
|
private static final Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Duration.class, (JsonDeserializer<Duration>) (json, typeOfT, context) -> Duration.parse(json.getAsString()))
|
||||||
|
.create();
|
||||||
|
|
||||||
|
public static class StartupEvent {
|
||||||
|
private Date startTime;
|
||||||
|
private Date endTime;
|
||||||
|
private Duration duration;
|
||||||
|
private StartupStep startupStep;
|
||||||
|
public Date getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
public void setStartTime(Date startTime) {
|
||||||
|
this.startTime = startTime;
|
||||||
|
}
|
||||||
|
public Date getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
public void setEndTime(Date endTime) {
|
||||||
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
public Duration getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
public void setDuration(Duration duration) {
|
||||||
|
this.duration = duration;
|
||||||
|
}
|
||||||
|
public StartupStep getStartupStep() {
|
||||||
|
return startupStep;
|
||||||
|
}
|
||||||
|
public void setStartupStep(StartupStep startupStep) {
|
||||||
|
this.startupStep = startupStep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StartupStep {
|
||||||
|
private String name;
|
||||||
|
private int id;
|
||||||
|
private int parentId;
|
||||||
|
private PropertyValuePair[] tags;
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public int getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
public void setParentId(int parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
public PropertyValuePair[] getTags() {
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
public void setTags(PropertyValuePair[] tags) {
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
String findPropertyValue(String key) {
|
||||||
|
for (PropertyValuePair pair : tags) {
|
||||||
|
if (key.equals(pair.getKey())) {
|
||||||
|
return pair.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PropertyValuePair {
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StartupModel parse(Map<?, ?> mapContent) {
|
||||||
|
Object timeline = mapContent.get("timeline");
|
||||||
|
if (timeline instanceof Map) {
|
||||||
|
Object events = ((Map<?,?>) timeline).get("events");
|
||||||
|
if (events instanceof List) {
|
||||||
|
List<StartupEvent> fromJson = gson.fromJson(gson.toJson(events), new TypeToken<List<StartupEvent>>() {}.getType());
|
||||||
|
return new StartupModel(fromJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<StartupEvent> startupEvents;
|
||||||
|
|
||||||
|
private Map<String, Duration> beanInstanciationTimes;
|
||||||
|
|
||||||
|
public StartupModel(List<StartupEvent> startupEvents) {
|
||||||
|
this.startupEvents = startupEvents;
|
||||||
|
beanInstanciationTimes = createbeanInstanciationTimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Duration> createbeanInstanciationTimes() {
|
||||||
|
Map<String, Duration> beanInstanciationTimes = new HashMap<>();
|
||||||
|
for (StartupEvent event : startupEvents) {
|
||||||
|
if (event.getStartupStep() != null && BEAN_INSTANCIATION_EVENT.equals(event.getStartupStep().getName())) {
|
||||||
|
String beanId = event.getStartupStep().findPropertyValue("beanName");
|
||||||
|
if (beanId != null) {
|
||||||
|
beanInstanciationTimes.put(beanId, event.getDuration());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return beanInstanciationTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getBeanInstanciationTime(String beanId) {
|
||||||
|
return beanInstanciationTimes.get(beanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StartupEvent> getStartupEvents() {
|
||||||
|
return startupEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.springframework.ide.vscode.boot.java.metrics.test;
|
||||||
|
|
||||||
|
public class StartupMetricsTest {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import org.springframework.ide.vscode.boot.java.livehover.v2.LiveMetricsModel;
|
|||||||
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties;
|
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties;
|
||||||
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
|
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
|
||||||
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMappingBoot1xRequestMapping;
|
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMappingBoot1xRequestMapping;
|
||||||
|
import org.springframework.ide.vscode.boot.java.livehover.v2.StartupModel;
|
||||||
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
|
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,6 +45,7 @@ public class SpringProcessLiveDataBuilder {
|
|||||||
private LiveConditional[] conditionals;
|
private LiveConditional[] conditionals;
|
||||||
private LiveProperties properties;
|
private LiveProperties properties;
|
||||||
private LiveMetricsModel metrics;
|
private LiveMetricsModel metrics;
|
||||||
|
private StartupModel startup;
|
||||||
|
|
||||||
public SpringProcessLiveDataBuilder processName(String processName) {
|
public SpringProcessLiveDataBuilder processName(String processName) {
|
||||||
this.processName = processName;
|
this.processName = processName;
|
||||||
@@ -129,8 +131,13 @@ public class SpringProcessLiveDataBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpringProcessLiveDataBuilder liveStartup(StartupModel startup) {
|
||||||
|
this.startup = startup;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SpringProcessLiveData build() {
|
public SpringProcessLiveData build() {
|
||||||
return new SpringProcessLiveData(processName, processID, contextPath, urlScheme, port, host, beansModel, activeProfiles, requestMappings, conditionals, properties, metrics);
|
return new SpringProcessLiveData(processName, processID, contextPath, urlScheme, port, host, beansModel, activeProfiles, requestMappings, conditionals, properties, metrics, startup);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user