-
Notifications
You must be signed in to change notification settings - Fork 205
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
Changes from 20 commits
bb3576b
8553cbd
1a0fa4e
edef438
aca4383
607b656
f147ff9
3430d26
efa1ff2
6bd5570
baba3b9
883fd72
912bae6
51f0258
704a11f
fd168aa
4c8d54e
6b8fe30
c31ff35
d844011
c8cff0e
63a6217
1e4ad3e
d33177c
757f0d1
7531583
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -119,7 +120,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); | ||
} | ||
|
||
private void setInternalLogger(SDKLoggerXmlElement sdkLogger, TelemetryConfiguration configuration) { | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
} | ||
|
||
List<TelemetryModule> pcModules = getPerformanceModules(appConfiguration.getPerformance()); | ||
|
||
modules.addAll(pcModules); | ||
} | ||
|
||
|
@@ -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; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package com.microsoft.applicationinsights.internal.heartbeat; | ||
|
||
import com.microsoft.applicationinsights.internal.logger.InternalLogger; | ||
import com.microsoft.applicationinsights.internal.util.PropertyHelper; | ||
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>() { | ||
|
||
volatile Set<String> enabledProperties = MiscUtils.except(defaultFields, disableFields); | ||
@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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want the OS version? If so, you should explore the difference between I also found a few articles stating that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can amend it, if there is a better way though I am not aware of one. This is meta data which is not very very vital though. |
||
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(); | ||
} | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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. #ResolvedThere was a problem hiding this comment.
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
fromsetMinimumConfiguration
. SincesetTelemetryModules
is always called andsetMinimumConfiguration
is only called when the XML config is missing, you don't need the add call insetMinimumConfiguration
. #ResolvedThere was a problem hiding this comment.
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
There was a problem hiding this comment.
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)