Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HeartBeat Feature Implementation #631

Merged
merged 26 commits into from
Apr 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb3576b
Initial heartbeat feature commit
dhaval24 Mar 27, 2018
8553cbd
adding required interfaces and main class to spin up Metric thread
dhaval24 Mar 27, 2018
1a0fa4e
some refactoring, implementing initialize for HeartBeatProvider, addi…
dhaval24 Mar 28, 2018
edef438
some refactoring adding properties collection for webapps
dhaval24 Mar 29, 2018
aca4383
fix unlocking
dhaval24 Mar 29, 2018
607b656
removing unnecessary variable creation, initial test cases added
dhaval24 Mar 30, 2018
f147ff9
adding more tests
dhaval24 Mar 30, 2018
3430d26
added support for heartbeat module to be added as default, refactored…
dhaval24 Mar 30, 2018
efa1ff2
added more test cases, some refactoring in tests, added javadocs
dhaval24 Mar 31, 2018
6bd5570
Merge branch 'master' into HeartBeat
dhaval24 Mar 31, 2018
baba3b9
updating test to remove try catch and adding throws
dhaval24 Mar 31, 2018
883fd72
updated changelog, added somemore java docs, renamed method to be mor…
dhaval24 Mar 31, 2018
912bae6
fix threadpool shutdown timing, add missing shutdown for a pool, chan…
dhaval24 Apr 1, 2018
51f0258
fixing proper locking
dhaval24 Apr 2, 2018
704a11f
removing caching of environment variable to prevent hotswap data read…
dhaval24 Apr 4, 2018
fd168aa
Heartbeat updates to accomodate autoconfig in SpringBoot
dhaval24 Apr 10, 2018
4c8d54e
Optimizig disabling heartbeat to improve performance, renaming isEnab…
dhaval24 Apr 12, 2018
6b8fe30
done some refactoring and polishing, addressed review comments
dhaval24 Apr 13, 2018
c31ff35
fix a broken test and polish
dhaval24 Apr 14, 2018
d844011
some behavior change in setProperties, polish docs, addressed remaini…
dhaval24 Apr 14, 2018
c8cff0e
addressing leftover
dhaval24 Apr 16, 2018
63a6217
done some refactoring, removed extra logging and polish
dhaval24 Apr 17, 2018
1e4ad3e
Renaming, removing volatile
dhaval24 Apr 17, 2018
d33177c
mockito for tests, removed redundant lock, add containsKey, public co…
dhaval24 Apr 17, 2018
757f0d1
refactor to use factoryMethod for single thread executor, moved shutd…
dhaval24 Apr 18, 2018
7531583
some minor changes and reverting gradle files changed
dhaval24 Apr 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# CHANGELOG

# Version 2.1.0
- Introduced Heartbeat feature which sends periodic heartbeats with basic information about application and runtime to Application Insights.

## Version 2.0.2
- Fix incorrect success flag set when capturing HTTP Dependency.
- Fix [#577](https://github.com/Microsoft/ApplicationInsights-Java/issues/577), removed HttpFactory class as it was not being used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,11 @@ public void setInstrumentationKey(String key) {

instrumentationKey = key;
}

/**
* Method for tear down in tests
*/
private static void setActiveAsNull() {
active = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package com.microsoft.applicationinsights.internal.config;

import com.microsoft.applicationinsights.internal.heartbeat.HeartBeatModule;
import java.io.InputStream;
import java.util.Map;
import java.util.List;
Expand Down Expand Up @@ -119,7 +120,9 @@ public final void initialize(TelemetryConfiguration configuration) {
private void setMinimumConfiguration(ApplicationInsightsXmlConfiguration userConfiguration, TelemetryConfiguration configuration) {
Copy link
Contributor

@littleaj littleaj Apr 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the heartbeat module always be added? It didn't look like it the way this method is called. #Resolved

Copy link
Contributor Author

@dhaval24 dhaval24 Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this module will always be added. #Resolved

Copy link
Contributor Author

@dhaval24 dhaval24 Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@littleaj look into the method setTelemetryModules(). It adds HB module explicitly and also look atsetMininmumConfiguration(). This module would be present by default. #Resolved

Copy link
Contributor

@littleaj littleaj Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhaval24 , Ok, I see.

I did notice that you can reduce some redundancy if you remove the call to addHeartbeatModule from setMinimumConfiguration. Since setTelemetryModules is always called and setMinimumConfiguration is only called when the XML config is missing, you don't need the add call in setMinimumConfiguration. #Resolved

Copy link
Contributor Author

@dhaval24 dhaval24 Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@littleaj I need to add this for SpringBoot scenarios. Here it is not called because SpringBoot autoconfigures it using beans. #Resolved

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I see. I missed the return statements above.


In reply to: 181910995 [](ancestors = 181910995)

setInstrumentationKey(userConfiguration, configuration);
configuration.setChannel(new InProcessTelemetryChannel());
addHeartBeatModule(configuration);
setContextInitializers(null, configuration);
initializeComponents(configuration);
}

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

//if heartbeat module is not loaded, load heartbeat module
if (!isHeartBeatModuleAdded(modules)) {
addHeartBeatModule(configuration);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this behavior common across AI SDKs implementing heartbeat? I would not want customers remember in which SDK they would need to add HeartBeat explicitly if replacing modules vs. where it will be added by default even if they specifically didn't mention it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Dmitry-Matveev in .net SDK DiagnosticModule is used to enable HeartBeat module and I believe DiagnosticModule is turned on by default. However, in Java SDK there is no diagnostic module and if we want to enable heartbeat module by default if not specified in the configuration file I think this would be the way. If you have better option I would like to hear that.
As far as behavior will go, both the SDKs would be consistent that means unless user has turned the HeartBeat module off explicitly in configuration it will be turned on by default with default settings.

}

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

modules.addAll(pcModules);
}

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

/**
* Adds heartbeat module with default configuration
* @param configuration TelemetryConfiguration Instance
*/
private void addHeartBeatModule(TelemetryConfiguration configuration) {
HeartBeatModule module = new HeartBeatModule(new HashMap<String, String>());
configuration.getTelemetryModules().add(module);
}

/**
* Checks if heartbeat module is present
* @param module List of modules in current TelemetryConfiguration Instance
* @return true if heartbeat module is present
*/
private boolean isHeartBeatModuleAdded(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,169 @@
package com.microsoft.applicationinsights.internal.heartbeat;

import com.microsoft.applicationinsights.internal.logger.InternalLogger;
import com.microsoft.applicationinsights.internal.util.PropertyHelper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import org.apache.commons.lang3.exception.ExceptionUtils;

/**
* <h1>Base Heartbeat Property Provider</h1>
*
* <p>
* This class is a concrete implementation of {@link HeartBeatPayloadProviderInterface}
* It enables setting SDK Metadata to heartbeat payload.
* </p>
*
* @author Dhaval Doshi
*/
public class DefaultHeartBeatPropertyProvider implements HeartBeatPayloadProviderInterface {

/**
* Collection holding default properties for this default provider.
*/
private final Set<String> defaultFields;

/**
* Random GUID that would help in analysis when app has stopped and restarted. Each restart will
* have a new GUID. If the application is unstable and goes through frequent restarts this will help
* us identify instability in the analytics backend.
*/
private static UUID uniqueProcessId;

/**
* Name of this provider.
*/
private final String name = "Default";

private final String JRE_VERSION = "jreVersion";

private final String SDK_VERSION = "sdkVersion";

private final String OS_VERSION = "osVersion";

private final String PROCESS_SESSION_ID = "processSessionId";

public DefaultHeartBeatPropertyProvider() {
defaultFields = new HashSet<>();
initializeDefaultFields(defaultFields);
}

@Override
public String getName() {
return this.name;
}

@Override
public boolean isKeyword(String keyword) {
return defaultFields.contains(keyword);
}

@Override
public Callable<Boolean> setDefaultPayload(final List<String> disableFields,
final HeartBeatProviderInterface provider) {
return new Callable<Boolean>() {

Set<String> enabledProperties = MiscUtils.except(disableFields, defaultFields);
@Override
public Boolean call() {
boolean hasSetValues = false;
for (String fieldName : enabledProperties) {
try {
switch (fieldName) {
case JRE_VERSION:
provider.addHeartBeatProperty(fieldName, getJreVersion(), true);
hasSetValues = true;
break;
case SDK_VERSION:
provider.addHeartBeatProperty(fieldName, getSdkVersion(), true);
hasSetValues = true;
break;
case OS_VERSION:
provider.addHeartBeatProperty(fieldName, getOsVersion(), true);
hasSetValues = true;
break;
case PROCESS_SESSION_ID:
provider.addHeartBeatProperty(fieldName, getProcessSessionId(), true);
hasSetValues = true;
break;
default:
//We won't accept unknown properties in default providers.
InternalLogger.INSTANCE.trace("Encountered unknown default property");
break;
}
}
catch (Exception e) {
InternalLogger.INSTANCE.warn("Failed to obtain heartbeat property, stack trace"
+ "is: %s", ExceptionUtils.getStackTrace(e));
}
}
return hasSetValues;
}
};
}

/**
* This method initializes the collection with Default Properties of this provider.
* @param defaultFields collection to hold default properties.
*/
private void initializeDefaultFields(Set<String> defaultFields) {

if (defaultFields == null) {
defaultFields = new HashSet<>();
}
defaultFields.add(JRE_VERSION);
defaultFields.add(SDK_VERSION);
defaultFields.add(OS_VERSION);
defaultFields.add(PROCESS_SESSION_ID);
}

/**
* Gets the JDK version being used by the application
* @return String representing JDK Version
*/
private String getJreVersion() {
return System.getProperty("java.version");
}

/**
* Returns the Application Insights SDK version user is using to instrument his application
* @return returns string prefixed with "java" representing the Application Insights Java
* SDK version.
*/
private String getSdkVersion() {

String sdkVersion = "java";

Properties sdkVersionProps = PropertyHelper.getSdkVersionProperties();
if (sdkVersionProps != null) {
String version = sdkVersionProps.getProperty("version");
sdkVersion = String.format("java:%s", version);
return sdkVersion;
}
return sdkVersion + ":unknown-version";
}

/**
* Gets the OS version on which application is running.
* @return String representing OS version
*/
private String getOsVersion() {
return System.getProperty("os.name");
}

/**
* Returns the Unique GUID for the application's current session.
* @return String representing GUID for each running session
*/
private String getProcessSessionId() {
if (uniqueProcessId == null) {
uniqueProcessId = UUID.randomUUID();
}
return uniqueProcessId.toString();
}
}
Loading