Startup metrics initial work (before unit test)

This commit is contained in:
BoykoAlex
2020-11-17 13:13:11 -05:00
parent d96e9b996d
commit ed68dcd47c
10 changed files with 212 additions and 8 deletions

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -185,6 +186,16 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
if (!wiredBeans.isEmpty()) {
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(definedBean.getId());

View File

@@ -18,7 +18,7 @@ public interface SpringProcessConnector {
String getProcessKey();
void connect() throws Exception;
SpringProcessLiveData refresh() throws Exception;
SpringProcessLiveData refresh(SpringProcessLiveData currentData) throws Exception;
void disconnect() throws Exception;
void addConnectorChangeListener(SpringProcessConnectionChangeListener listener);

View File

@@ -107,7 +107,7 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector {
}
@Override
public SpringProcessLiveData refresh() throws Exception {
public SpringProcessLiveData refresh(SpringProcessLiveData currentData) throws Exception {
log.info("try to open JMX connection to: " + jmxURL);
if (jmxConnection != null) {
@@ -119,7 +119,7 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector {
}
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) {
this.processID = liveData.getProcessID();

View File

@@ -210,7 +210,7 @@ public class SpringProcessConnectorService {
try {
progressTask.progressEvent(progressMessage);
SpringProcessLiveData newLiveData = connector.refresh();
SpringProcessLiveData newLiveData = connector.refresh(this.liveDataProvider.getCurrent(processKey));
if (newLiveData != null) {
if (!this.liveDataProvider.add(processKey, newLiveData)) {

View File

@@ -29,11 +29,12 @@ public class SpringProcessLiveData {
private final LiveConditional[] conditionals;
private final LiveProperties properties;
private final LiveMetricsModel metrics;
private StartupModel startup;
public SpringProcessLiveData(String processName, String processID, String contextPath, String urlScheme,
String port, String host, LiveBeansModel beansModel, String[] activeProfiles,
LiveRequestMapping[] requestMappings, LiveConditional[] conditionals, LiveProperties properties,
LiveMetricsModel metrics) {
LiveMetricsModel metrics, StartupModel startup) {
super();
this.processName = processName;
this.processID = processID;
@@ -47,6 +48,8 @@ public class SpringProcessLiveData {
this.conditionals = conditionals;
this.properties = properties;
this.metrics = metrics;
this.startup = startup;
}
public String getProcessName() {
@@ -97,4 +100,8 @@ public class SpringProcessLiveData {
return this.metrics;
}
public StartupModel getStartup() {
return startup;
}
}

View File

@@ -17,6 +17,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
@@ -61,9 +62,10 @@ public class SpringProcessLiveDataExtractorOverJMX {
* @param host should always be != null
* @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 currentData currently stored live data
*/
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 {
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
@@ -91,6 +93,7 @@ public class SpringProcessLiveDataExtractorOverJMX {
LiveRequestMapping[] requestMappings = getRequestMappings(connection, domain);
LiveBeansModel beans = getBeans(connection, domain);
LiveMetricsModel metrics = getMetrics(connection, domain);
StartupModel startup = getStartup(connection, domain, currentData == null ? null : currentData.getStartup());
if (contextPath == null) {
contextPath = getContextPath(connection, domain, environment);
@@ -112,7 +115,8 @@ public class SpringProcessLiveDataExtractorOverJMX {
requestMappings,
conditionals,
properties,
metrics);
metrics,
startup);
}
catch (Exception 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) {
try {

View File

@@ -78,5 +78,9 @@ public class SpringProcessLiveDataProvider {
listener.liveDataChanged(event);
}
}
SpringProcessLiveData getCurrent(String processKey) {
return this.liveData.get(processKey);
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,5 @@
package org.springframework.ide.vscode.boot.java.metrics.test;
public class StartupMetricsTest {
}

View File

@@ -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.LiveRequestMapping;
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;
/**
@@ -44,6 +45,7 @@ public class SpringProcessLiveDataBuilder {
private LiveConditional[] conditionals;
private LiveProperties properties;
private LiveMetricsModel metrics;
private StartupModel startup;
public SpringProcessLiveDataBuilder processName(String processName) {
this.processName = processName;
@@ -129,8 +131,13 @@ public class SpringProcessLiveDataBuilder {
return this;
}
public SpringProcessLiveDataBuilder liveStartup(StartupModel startup) {
this.startup = startup;
return this;
}
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);
}
}