Skip to content

Commit

Permalink
added support for heartbeat module to be added as default, refactored…
Browse files Browse the repository at this point in the history
… sometest, added more tests, added mocks as required
  • Loading branch information
dhaval24 committed Mar 30, 2018
1 parent f147ff9 commit 3430d26
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

package com.microsoft.applicationinsights.internal.config;

import com.microsoft.applicationinsights.internal.heartbeat.HeartBeatModule;
import com.microsoft.applicationinsights.telemetry.Telemetry;
import java.io.InputStream;
import java.util.Map;
import java.util.List;
Expand Down Expand Up @@ -119,7 +121,9 @@ public final void initialize(TelemetryConfiguration configuration) {
private void setMinimumConfiguration(ApplicationInsightsXmlConfiguration userConfiguration, TelemetryConfiguration configuration) {
setInstrumentationKey(userConfiguration, configuration);
configuration.setChannel(new InProcessTelemetryChannel());
addHeartBeatModule(configuration);
setContextInitializers(null, configuration);
initializeComponents(configuration);

This comment has been minimized.

Copy link
@gavlyukovskiy

gavlyukovskiy Mar 31, 2018

Contributor

@dhaval24 Spring boot also does component initialization when creating TelemetryConfiguration bean, won't it break something if module is initialized twice?

This comment has been minimized.

Copy link
@dhaval24

dhaval24 Mar 31, 2018

Author Contributor

@gavlyukovskiy I think initialization code will run only once. I might be wrong. If you look at the class HeartBeatModule, it has a variable isEnabled which is static. And initialization happens with a check on that, so I believe initialization would be called twice but won't happen. So should work, though would need to test and see if something breaks. Still also would need to write autoconfigure logic for this in Starter code also :)

}

private void setInternalLogger(SDKLoggerXmlElement sdkLogger, TelemetryConfiguration configuration) {
Expand Down Expand Up @@ -173,7 +177,13 @@ private void setTelemetryModules(ApplicationInsightsXmlConfiguration appConfigur
ReflectionUtils.loadComponents(TelemetryModule.class, modules, configurationModules.getAdds());
}

//if heartbeat module is not loaded, load heartbeat module
if (!heartBeatModuleAdded(modules)) {
addHeartBeatModule(configuration);
}

List<TelemetryModule> pcModules = getPerformanceModules(appConfiguration.getPerformance());

modules.addAll(pcModules);
}

Expand Down Expand Up @@ -502,6 +512,18 @@ private void initializeComponents(TelemetryConfiguration configuration) {
}
}

private void addHeartBeatModule(TelemetryConfiguration configuration) {
HeartBeatModule module = new HeartBeatModule(new HashMap<String, String>());
configuration.getTelemetryModules().add(module);
}

private boolean heartBeatModuleAdded(List<TelemetryModule> module) {
for (TelemetryModule mod : module) {
if (mod instanceof HeartBeatModule) return true;
}
return false;
}

void setPerformanceCountersSection(String performanceCountersSection) {
this.performanceCountersSection = performanceCountersSection;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.microsoft.applicationinsights.internal.heartbeat;

import com.microsoft.applicationinsights.TelemetryConfiguration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class HeartBeatProviderMock implements HeartBeatProviderInterface {

private ConcurrentMap<String, HeartBeatPropertyPayload> heartbeatProperties;

private List<String> disableDefaultProperties = new ArrayList<>();

private List<String> disabledHeartBeatPropertiesProviders = new ArrayList<>();

private String instrumentationKey;

private volatile boolean isEnabled;

private long interval;

public HeartBeatProviderMock() {
this.heartbeatProperties = new ConcurrentHashMap<>();
this.instrumentationKey = UUID.randomUUID().toString();
this.isEnabled = true;
this.interval = 31;
}

public ConcurrentMap<String, HeartBeatPropertyPayload> getHeartBeatProperties() {
return this.heartbeatProperties;

}
@Override
public String getInstrumentationKey() {
return this.instrumentationKey;
}

@Override
public void setInstrumentationKey(String key) {

}

@Override
public void initialize(TelemetryConfiguration configuration) {

}

@Override
public boolean addHeartBeatProperty(String propertyName, String propertyValue,
boolean isHealthy) {
HeartBeatPropertyPayload payload = new HeartBeatPropertyPayload();
payload.setHealthy(isHealthy);
payload.setPayloadValue(propertyValue);
heartbeatProperties.put(propertyName, payload);
return true;
}

@Override
public boolean setHeartBeatProperty(String propertyName, String propertyValue,
boolean isHealthy) {
if (heartbeatProperties.containsKey(propertyName)) {
HeartBeatPropertyPayload payload = new HeartBeatPropertyPayload();
payload.setHealthy(isHealthy);
payload.setPayloadValue(propertyValue);
heartbeatProperties.put(propertyName, payload);
return true;
}
return false;
}

@Override
public boolean isHeartBeatEnabled() {
return this.isEnabled;
}

@Override
public void setHeartBeatEnabled(boolean isEnabled) {

}

@Override
public List<String> getExcludedHeartBeatPropertyProviders() {
return this.disabledHeartBeatPropertiesProviders;
}

@Override
public void setExcludedHeartBeatPropertyProviders(
List<String> excludedHeartBeatPropertyProviders) {

}

@Override
public long getHeartBeatInterval() {
return this.interval;
}

@Override
public void setHeartBeatInterval(long timeUnit) {

}

@Override
public List<String> getExcludedHeartBeatProperties() {
return this.disableDefaultProperties;
}

@Override
public void setExcludedHeartBeatProperties(List<String> excludedHeartBeatProperties) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@
import com.microsoft.applicationinsights.TelemetryClient;
import com.microsoft.applicationinsights.TelemetryConfiguration;
import com.microsoft.applicationinsights.extensibility.TelemetryModule;
import com.microsoft.applicationinsights.telemetry.MetricTelemetry;
import com.microsoft.applicationinsights.telemetry.Telemetry;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import org.junit.Assert;
import org.junit.Test;

Expand Down Expand Up @@ -42,7 +49,6 @@ public void initializeHeartBeatWithNonDefaultIntervalSetsCorrectly() {
HeartBeatModule module = new HeartBeatModule(dummyPropertiesMap);
module.initialize(null);
Assert.assertEquals(heartBeatInterval, module.getHeartBeatInterval());

}

@Test
Expand Down Expand Up @@ -74,44 +80,54 @@ public void canExtendHeartBeatPayload() {

@Test
public void initializationOfTelemetryClientDoesNotResetHeartbeat() {
HeartBeatModule hbm = new HeartBeatModule(new HashMap<String, String>());
TelemetryConfiguration configuration= new TelemetryConfiguration();
configuration.getTelemetryModules().add(hbm);
hbm.initialize(configuration);
TelemetryClient client = new TelemetryClient(configuration);
TelemetryClient client = new TelemetryClient();

boolean origIsEnabled = true;
String origExcludedHbProvider = "FakeProvider";
long orignalInterval = 0;
long setInterval = 30;

for (TelemetryModule module : configuration.getTelemetryModules()) {
for (TelemetryModule module : TelemetryConfiguration.getActive().getTelemetryModules()) {
if (module instanceof HeartBeatModule) {
origIsEnabled = ((HeartBeatModule) module).isHeartBeatEnabled();
((HeartBeatModule) module).setHeartBeatEnabled(!origIsEnabled);

Assert.assertFalse(hbm.getExcludedHeartBeatProperties().contains(origExcludedHbProvider));
hbm.setExcludedHeartBeatPropertiesProvider(Arrays.asList(origExcludedHbProvider));
orignalInterval = hbm.getHeartBeatInterval();
hbm.setExcludedHeartBeatProperties(Arrays.asList("test01"));
hbm.setHeartBeatInterval(setInterval);
Assert.assertFalse(((HeartBeatModule)module).getExcludedHeartBeatProperties().contains(origExcludedHbProvider));
((HeartBeatModule)module).setExcludedHeartBeatPropertiesProvider(Arrays.asList(origExcludedHbProvider));
orignalInterval = ((HeartBeatModule)module).getHeartBeatInterval();
((HeartBeatModule)module).setExcludedHeartBeatProperties(Arrays.asList("test01"));
((HeartBeatModule)module).setHeartBeatInterval(setInterval);
}
}

TelemetryClient client2 = new TelemetryClient(configuration);
for (TelemetryModule module :configuration.getTelemetryModules()) {
TelemetryClient client2 = new TelemetryClient();
for (TelemetryModule module :TelemetryConfiguration.getActive().getTelemetryModules()) {
if (module instanceof HeartBeatModule) {
Assert.assertNotEquals(hbm.isHeartBeatEnabled(), origIsEnabled);
Assert.assertTrue(hbm.getExcludedHeartBeatPropertiesProvider().contains(origExcludedHbProvider));
Assert.assertTrue(hbm.getExcludedHeartBeatProperties().contains("test01"));
Assert.assertNotEquals(hbm.getHeartBeatInterval(), orignalInterval);
Assert.assertEquals(hbm.getHeartBeatInterval(), setInterval);
Assert.assertNotEquals(((HeartBeatModule)module).isHeartBeatEnabled(), origIsEnabled);
Assert.assertTrue(((HeartBeatModule)module).getExcludedHeartBeatPropertiesProvider().contains(origExcludedHbProvider));
Assert.assertTrue(((HeartBeatModule)module).getExcludedHeartBeatProperties().contains("test01"));
Assert.assertNotEquals(((HeartBeatModule)module).getHeartBeatInterval(), orignalInterval);
Assert.assertEquals(((HeartBeatModule)module).getHeartBeatInterval(), setInterval);
}
}
}

@Test
public void heartBeatIsEnabledByDefault() {
TelemetryClient client = new TelemetryClient();
List<TelemetryModule> modules = TelemetryConfiguration.getActive().getTelemetryModules();
boolean hasHeartBeatModule = false;
HeartBeatModule hbm = null;
for (TelemetryModule m : modules) {
if (m instanceof HeartBeatModule) {
hasHeartBeatModule = true;
hbm = (HeartBeatModule)m;
break;
}
}
Assert.assertTrue(hasHeartBeatModule);
Assert.assertNotNull(hbm);
Assert.assertTrue(hbm.isHeartBeatEnabled());

}

Expand Down Expand Up @@ -154,6 +170,98 @@ public void canDisableHeartBeatPropertyProviderPriorToInitialize() {
catch (Exception e) {
e.printStackTrace();
}
}

@Test
public void defaultHeartbeatPropertyProviderSendsNoFieldWhenDisabled() {
HeartBeatProviderMock mockProvider = new HeartBeatProviderMock();
List<String> disabledProviders = new ArrayList<>();
disabledProviders.add("Base");
disabledProviders.add("webapps");
Callable<Boolean> callable = HeartbeatDefaultPayload.populateDefaultPayload(new ArrayList<String>(),
disabledProviders, mockProvider);
try {
callable.call();
Assert.assertEquals(0, mockProvider.getHeartBeatProperties().size());
} catch (Exception e) {
e.printStackTrace();

This comment has been minimized.

Copy link
@gavlyukovskiy

gavlyukovskiy Mar 31, 2018

Contributor

@dhaval24 I think it's better to add throws Exception to the method signature rather than printing stacktrace, if you catch exception and print stacktrace test will be considered as passed which is quite dangerous.

This comment has been minimized.

Copy link
@dhaval24

dhaval24 Mar 31, 2018

Author Contributor

@gavlyukovskiy that's good suggestion. I will make changes.

This comment has been minimized.

Copy link
@littleaj

littleaj Mar 31, 2018

Contributor

Agreed. Generally, try/catch shouldn't be in tests unless you are testing exception behavior.

}
}

@Test
public void heartBeatPayloadContainsDataByDefault() {
HeartBeatProvider provider = new HeartBeatProvider();
provider.initialize(null);
try {
Method m = provider.getClass().getDeclaredMethod("gatherData");
m.setAccessible(true);
Telemetry t = (Telemetry)m.invoke(provider);
Assert.assertNotNull(t);
} catch (Exception e) {
e.printStackTrace();
}
}

@Test
public void heartBeatPayloadContainsSpecificProperties() {
HeartBeatProvider provider = new HeartBeatProvider();
Assert.assertTrue(provider.addHeartBeatProperty("test", "testVal", true));
try {
Method m = provider.getClass().getDeclaredMethod("gatherData");
m.setAccessible(true);
Telemetry t = (Telemetry)m.invoke(provider);
Assert.assertEquals("testVal", t.getProperties().get("test"));
} catch (Exception e) {
e.printStackTrace();
}
}

@Test
public void heartbeatMetricIsNonZeroWhenFailureConditionPresent() {
HeartBeatProvider provider = new HeartBeatProvider();
Assert.assertTrue(provider.addHeartBeatProperty("test", "testVal", false));
try {
Method m = provider.getClass().getDeclaredMethod("gatherData");
m.setAccessible(true);
Telemetry t = (Telemetry)m.invoke(provider);
Assert.assertEquals(1, ((MetricTelemetry)t).getValue(), 0.0);
} catch (Exception e) {
e.printStackTrace();
}
}

@Test
public void heartbeatMetricCountsForAllFailures() {
HeartBeatProvider provider = new HeartBeatProvider();
Assert.assertTrue(provider.addHeartBeatProperty("test", "testVal", false));
Assert.assertTrue(provider.addHeartBeatProperty("test1", "testVal1", false));
try {
Method m = provider.getClass().getDeclaredMethod("gatherData");
m.setAccessible(true);
Telemetry t = (Telemetry)m.invoke(provider);
Assert.assertEquals(2, ((MetricTelemetry)t).getValue(), 0.0);
} catch (Exception e) {
e.printStackTrace();
}
}

@Test
public void sentHeartbeatContainsExpectedDefaultFields() {
HeartBeatProviderMock mock = new HeartBeatProviderMock();
BaseDefaultHeartbeatPropertyProvider defaultProvider = new BaseDefaultHeartbeatPropertyProvider();
try {
HeartbeatDefaultPayload.populateDefaultPayload(new ArrayList<String>(), new ArrayList<String>(),
mock).call();
Field field = defaultProvider.getClass().getDeclaredField("defaultFields");
field.setAccessible(true);
Set<String> defaultFields = (Set<String>)field.get(defaultProvider);
for (String fieldName : defaultFields) {
Assert.assertTrue(mock.getHeartBeatProperties().containsKey(fieldName));
}
} catch (Exception e) {
e.printStackTrace();
}
}


}

0 comments on commit 3430d26

Please sign in to comment.