exclude) {
+ this.exclude = exclude;
+ }
+ }
+ }
+
+ static class Web {
+ /**
+ * Enables Web telemetry modules.
+ *
+ * Implicitly affects modules: - {@link WebRequestTrackingTelemetryModule} - {@link
+ * WebSessionTrackingTelemetryModule} - {@link WebUserTrackingTelemetryModule} - {@link
+ * WebPerformanceCounterModule} - {@link WebOperationIdTelemetryInitializer} - {@link
+ * WebOperationNameTelemetryInitializer} - {@link WebSessionTelemetryInitializer} - {@link
+ * WebUserTelemetryInitializer} - {@link WebUserAgentTelemetryInitializer}
+ *
+ *
False means that all those modules will be disabled regardless of the enabled property of
+ * concrete module.
+ */
+ private boolean enabled = true;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+ }
+
+ public static class QuickPulse {
+ /** Enables Quick Pulse integration. */
+ private boolean enabled = true;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+ }
+
+ static class Logger {
+ /** Type of application insights logger. */
+ private LoggerOutputType type = LoggerOutputType.CONSOLE;
+ /** Minimal level of application insights logger. */
+ private LoggingLevel level = LoggingLevel.OFF;
+
+ public LoggerOutputType getType() {
+ return type;
+ }
+
+ public void setType(LoggerOutputType type) {
+ this.type = type;
+ }
+
+ public LoggingLevel getLevel() {
+ return level;
+ }
+
+ public void setLevel(LoggingLevel level) {
+ this.level = level;
+ }
+ }
+
+ static class PerformanceCounter {
+
+ /** Default collection frequency of performance counters */
+ private long collectionFrequencyInSeconds =
+ PerformanceCounterContainer.DEFAULT_COLLECTION_FREQUENCY_IN_SEC;
+
+ public long getCollectionFrequencyInSeconds() {
+ return collectionFrequencyInSeconds;
+ }
+
+ public void setCollectionFrequencyInSeconds(long collectionFrequencyInSeconds) {
+ this.collectionFrequencyInSeconds = collectionFrequencyInSeconds;
+ }
+ }
+
+ static class Jmx {
+
+ /** List of JMX counters */
+ List jmxCounters = new ArrayList<>();
+
+ public List getJmxCounters() {
+ return jmxCounters;
+ }
+
+ public void setJmxCounters(List jmxCounters) {
+ this.jmxCounters = jmxCounters;
+ }
+ }
+
+ static class HeartBeat {
+
+ /**
+ * Switch to enable / disable heartbeat
+ */
+ boolean enabled = false;
+
+ /**
+ * The heartbeat interval in seconds.
+ */
+ long heartBeatInterval = HeartBeatProvider.DEFAULT_HEARTBEAT_INTERVAL;
+
+ /**
+ * List of excluded heartbeat properties
+ */
+ List excludedHeartBeatProviderList = new ArrayList<>();
+
+ /**
+ * List of excluded heartbeat providers
+ */
+ List excludedHeartBeatPropertiesList = new ArrayList<>();
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public long getHeartBeatInterval() {
+ return heartBeatInterval;
+ }
+
+ public void setHeartBeatInterval(long heartBeatInterval) {
+ this.heartBeatInterval = heartBeatInterval;
+ }
+
+ public List getExcludedHeartBeatProviderList() {
+ return excludedHeartBeatProviderList;
+ }
+
+ public void setExcludedHeartBeatProviderList(List excludedHeartBeatProviderList) {
+ this.excludedHeartBeatProviderList = excludedHeartBeatProviderList;
+ }
+
+ public List getExcludedHeartBeatPropertiesList() {
+ return excludedHeartBeatPropertiesList;
+ }
+
+ public void setExcludedHeartBeatPropertiesList(List excludedHeartBeatPropertiesList) {
+ this.excludedHeartBeatPropertiesList = excludedHeartBeatPropertiesList;
+ }
+ }
+
+ /**
+ * This method is used to process and load list of JmxCounters provided in the configuration.
+ * @param jmxCounterList
+ */
+ void processAndLoadJmxCounters(List jmxCounterList) {
+
+ try {
+ Map> data = new HashMap<>();
+ for (String jmxCounter : jmxCounterList) {
+ CompositeJmxData compositeJmxData = convertToCompositeJmxData(jmxCounter);
+ if (compositeJmxData == null) {
+ InternalLogger.INSTANCE.warn("unable to add Jmx counter %s", jmxCounter);
+ } else {
+ List collection = data.get(compositeJmxData.getObjectName());
+ if (collection == null) {
+ collection = new ArrayList<>();
+ data.put(compositeJmxData.getObjectName(), collection);
+ }
+ collection.add(new JmxAttributeData(compositeJmxData.getDisplayName(),
+ compositeJmxData.getAttributeName(), compositeJmxData.getType()));
+ }
+ }
+
+ //Register each entry in performance counter container
+ for (Map.Entry> entry : data.entrySet()) {
+ try {
+ if (PerformanceCounterContainer.INSTANCE.register(new JmxMetricPerformanceCounter(
+ entry.getKey(), entry.getKey(), entry.getValue()
+ ))) {
+ InternalLogger.INSTANCE.trace("Registered Jmx performance counter %s",
+ entry.getKey());
+ }
+ else {
+ InternalLogger.INSTANCE.trace("Failed to register Jmx performance"
+ + " counter %s", entry.getKey());
+ }
+ }
+ catch (Exception e) {
+ InternalLogger.INSTANCE.warn("Failed to register Jmx performance counter,"
+ + " of object name %s Stack trace is %s", entry.getKey(), ExceptionUtils.getStackTrace(e));
+ }
+ }
+ }
+ catch (Exception e) {
+ InternalLogger.INSTANCE.warn("Unable to add Jmx performance counter. Exception is"
+ + " %s", ExceptionUtils.getStackTrace(e));
+ }
+ }
+
+ /**
+ * This Internal class is used to represent the Jmx Object Structure
+ */
+ private class CompositeJmxData {
+ String displayName;
+ String objectName;
+ String attributeName;
+ String type;
+
+ String getDisplayName() {
+ return displayName;
+ }
+
+ void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ String getObjectName() {
+ return objectName;
+ }
+
+ void setObjectName(String objectName) {
+ this.objectName = objectName;
+ }
+
+ String getAttributeName() {
+ return attributeName;
+ }
+
+ void setAttributeName(String attributeName) {
+ this.attributeName = attributeName;
+ }
+
+ String getType() {
+ return type;
+ }
+
+ void setType(String type) {
+ this.type = type;
+ if (this.type != null) {
+ this.type = this.type.toUpperCase();
+ }
+ }
+ }
+
+ /**
+ * This converts jmxCounter String to {@link CompositeJmxData} object
+ * @param jmxCounter
+ * @return CompositeJmxData object
+ */
+ private CompositeJmxData convertToCompositeJmxData(String jmxCounter) {
+ if (jmxCounter != null && jmxCounter.length() > 0) {
+ String[] attributes = jmxCounter.split("/");
+ if (attributes.length < 3) {
+ InternalLogger.INSTANCE.warn("Missing either objectName or attributeName or"
+ + " display name. Jmx counter %s will not be added" , jmxCounter);
+ return null;
+ }
+ CompositeJmxData data = new CompositeJmxData();
+ for (int i = 0; i < attributes.length; ++i) {
+ if (i > 3) break;
+ if (i == 0) {
+ data.setObjectName(attributes[0]);
+ }
+ else if (i == 1) {
+ data.setAttributeName(attributes[1]);
+ }
+ else if (i == 2) {
+ data.setDisplayName(attributes[2]);
+ }
+ else {
+ data.setType(attributes[3]);
+ }
+ }
+ return data;
+ }
+ return null;
+ }
+}
diff --git a/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsTelemetryAutoConfiguration.java b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsTelemetryAutoConfiguration.java
new file mode 100644
index 00000000000..800e4caaa25
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsTelemetryAutoConfiguration.java
@@ -0,0 +1,194 @@
+/*
+ * ApplicationInsights-Java
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the ""Software""), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package com.microsoft.applicationinsights.boot;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import com.microsoft.applicationinsights.TelemetryClient;
+import com.microsoft.applicationinsights.TelemetryConfiguration;
+import com.microsoft.applicationinsights.boot.ApplicationInsightsProperties.Channel.InProcess;
+import com.microsoft.applicationinsights.boot.ApplicationInsightsProperties.TelemetryProcessor.Sampling;
+import com.microsoft.applicationinsights.channel.TelemetryChannel;
+import com.microsoft.applicationinsights.channel.concrete.inprocess.InProcessTelemetryChannel;
+import com.microsoft.applicationinsights.extensibility.ContextInitializer;
+import com.microsoft.applicationinsights.extensibility.TelemetryInitializer;
+import com.microsoft.applicationinsights.extensibility.TelemetryModule;
+import com.microsoft.applicationinsights.extensibility.TelemetryProcessor;
+import com.microsoft.applicationinsights.internal.channel.samplingV2.FixedRateSamplingTelemetryProcessor;
+import com.microsoft.applicationinsights.internal.logger.InternalLogger;
+import com.microsoft.applicationinsights.internal.perfcounter.PerformanceCounterContainer;
+import com.microsoft.applicationinsights.internal.quickpulse.QuickPulse;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Import;
+
+/**
+ * The central class for configuring and creating initialized {@link TelemetryConfiguration}
+ *
+ * @author Arthur Gavlyukovskiy, Dhaval Doshi
+ */
+@Configuration
+@ConditionalOnProperty(value = "azure.application-insights.instrumentation-key")
+@EnableConfigurationProperties(ApplicationInsightsProperties.class)
+@ConditionalOnClass(TelemetryConfiguration.class)
+@Import({
+ ApplicationInsightsModuleConfiguration.class,
+ ApplicationInsightsWebModuleConfiguration.class
+})
+public class ApplicationInsightsTelemetryAutoConfiguration {
+
+ private static final Logger log = getLogger(ApplicationInsightsTelemetryAutoConfiguration.class);
+
+ private ApplicationInsightsProperties applicationInsightsProperties;
+
+ private Collection contextInitializers;
+
+ private Collection telemetryInitializers;
+
+ private Collection telemetryModules;
+
+ private Collection telemetryProcessors;
+
+ @Autowired
+ public ApplicationInsightsTelemetryAutoConfiguration(
+ ApplicationInsightsProperties applicationInsightsProperties) {
+ this.applicationInsightsProperties = applicationInsightsProperties;
+ }
+
+ @Autowired(required = false)
+ public void setContextInitializers(
+ Collection contextInitializers) {
+ this.contextInitializers = contextInitializers;
+ }
+
+ @Autowired(required = false)
+ public void setTelemetryInitializers(
+ Collection telemetryInitializers) {
+ this.telemetryInitializers = telemetryInitializers;
+ }
+
+ @Autowired(required = false)
+ public void setTelemetryModules(
+ Collection telemetryModules) {
+ this.telemetryModules = telemetryModules;
+ }
+
+ @Autowired(required = false)
+ public void setTelemetryProcessors(
+ Collection telemetryProcessors) {
+ this.telemetryProcessors = telemetryProcessors;
+ }
+
+ @Bean
+ @DependsOn("internalLogger")
+ public TelemetryConfiguration telemetryConfiguration(TelemetryChannel telemetryChannel) {
+ TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.getActiveWithoutInitializingConfig();
+ telemetryConfiguration.setTrackingIsDisabled(!applicationInsightsProperties.isEnabled());
+ telemetryConfiguration.setInstrumentationKey(applicationInsightsProperties.getInstrumentationKey());
+ if (contextInitializers != null) {
+ telemetryConfiguration.getContextInitializers().addAll(contextInitializers);
+ }
+ if (telemetryInitializers != null) {
+ telemetryConfiguration.getTelemetryInitializers().addAll(telemetryInitializers);
+ }
+ if (telemetryModules != null) {
+ telemetryConfiguration.getTelemetryModules().addAll(telemetryModules);
+ }
+ if (telemetryProcessors != null) {
+ telemetryConfiguration.getTelemetryProcessors().addAll(telemetryProcessors);
+ }
+ telemetryConfiguration.setChannel(telemetryChannel);
+ initializeComponents(telemetryConfiguration);
+ return telemetryConfiguration;
+ }
+
+ // TODO: copy-paste from TelemetryConfigurationFactory, move to TelemetryConfiguration?
+ private void initializeComponents(TelemetryConfiguration configuration) {
+ List telemetryModules = configuration.getTelemetryModules();
+
+ for (TelemetryModule module : telemetryModules) {
+ try {
+ module.initialize(configuration);
+ }
+ catch (Exception e) {
+ log.error("Failed to initialized telemetry module " + module.getClass().getSimpleName(), e);
+ }
+ }
+ }
+
+ @Bean
+ public TelemetryClient telemetryClient(TelemetryConfiguration configuration) {
+ return new TelemetryClient(configuration);
+ }
+
+
+
+ @Bean
+ @ConditionalOnMissingBean
+ public TelemetryChannel telemetryChannel() {
+ InProcess inProcess = applicationInsightsProperties.getChannel().getInProcess();
+ return new InProcessTelemetryChannel(inProcess.getEndpointAddress(),
+ String.valueOf(inProcess.getMaxTransmissionStorageFilesCapacityInMb()), inProcess.isDeveloperMode(),
+ inProcess.getMaxTelemetryBufferCapacity(), inProcess.getFlushIntervalInSeconds(), inProcess.isThrottling(),
+ inProcess.getMaxInstantRetry());
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.quick-pulse.enabled", havingValue = "true", matchIfMissing = true)
+ @DependsOn("telemetryConfiguration")
+ public QuickPulse quickPulse() {
+ QuickPulse.INSTANCE.initialize();
+ return QuickPulse.INSTANCE;
+ }
+
+ @Bean
+ public InternalLogger internalLogger() {
+ Map loggerParameters = new HashMap<>();
+ ApplicationInsightsProperties.Logger logger = applicationInsightsProperties.getLogger();
+ loggerParameters.put("Level", logger.getLevel().name());
+ InternalLogger.INSTANCE.initialize(logger.getType().name(), loggerParameters);
+ return InternalLogger.INSTANCE;
+ }
+
+ @Bean
+ public PerformanceCounterContainer performanceCounterContainer() {
+ ApplicationInsightsProperties.PerformanceCounter performanceCounter = applicationInsightsProperties.getPerformanceCounter();
+ PerformanceCounterContainer.INSTANCE.setCollectionFrequencyInSec(performanceCounter.getCollectionFrequencyInSeconds());
+
+ ApplicationInsightsProperties.Jmx jmx = applicationInsightsProperties.getJmx();
+ if (jmx.getJmxCounters() !=null && jmx.getJmxCounters().size() > 0) {
+ applicationInsightsProperties.processAndLoadJmxCounters(jmx.getJmxCounters());
+ }
+ return PerformanceCounterContainer.INSTANCE;
+ }
+}
diff --git a/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebModuleConfiguration.java b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebModuleConfiguration.java
new file mode 100644
index 00000000000..42e77421152
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebModuleConfiguration.java
@@ -0,0 +1,142 @@
+/*
+ * ApplicationInsights-Java
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the ""Software""), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package com.microsoft.applicationinsights.boot;
+
+import com.microsoft.applicationinsights.web.extensibility.initializers.WebOperationIdTelemetryInitializer;
+import com.microsoft.applicationinsights.web.extensibility.initializers.WebOperationNameTelemetryInitializer;
+import com.microsoft.applicationinsights.web.extensibility.initializers.WebSessionTelemetryInitializer;
+import com.microsoft.applicationinsights.web.extensibility.initializers.WebUserAgentTelemetryInitializer;
+import com.microsoft.applicationinsights.web.extensibility.initializers.WebUserTelemetryInitializer;
+import com.microsoft.applicationinsights.web.extensibility.modules.WebRequestTrackingTelemetryModule;
+import com.microsoft.applicationinsights.web.extensibility.modules.WebSessionTrackingTelemetryModule;
+import com.microsoft.applicationinsights.web.extensibility.modules.WebUserTrackingTelemetryModule;
+import com.microsoft.applicationinsights.web.internal.perfcounter.WebPerformanceCounterModule;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+/**
+ * Web Configuration for Application Insights
+ *
+ *
+ * This class provides the configuration for applications of type web. The modules in this class
+ * will only be configured if the Spring Framework identifies them as web Application.
+ *
+ *
+ * {@link Configuration} for web applications.
+ *
+ * @author Arthur Gavlyukovskiy
+ */
+@Configuration
+@ConditionalOnProperty(value = "azure.application-insights.web.enabled", havingValue = "true", matchIfMissing = true)
+@ConditionalOnWebApplication
+public class ApplicationInsightsWebModuleConfiguration {
+
+ /**
+ * Bean for WebRequestTrackingTelemetryModule
+ * @return instance of {@link WebRequestTrackingTelemetryModule}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebRequestTrackingTelemetryModule.enabled", havingValue = "true", matchIfMissing = true)
+ public WebRequestTrackingTelemetryModule webRequestTrackingTelemetryModule() {
+ return new WebRequestTrackingTelemetryModule();
+ }
+
+ /**
+ * Bean for WebSessionTrackingTelemetryModule
+ * @return instance of {@link WebSessionTrackingTelemetryModule}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebSessionTrackingTelemetryModule.enabled", havingValue = "true", matchIfMissing = true)
+ public WebSessionTrackingTelemetryModule webSessionTrackingTelemetryModule() {
+ return new WebSessionTrackingTelemetryModule();
+ }
+
+ /**
+ * Bean for WebUserTrackingTelemetryModule
+ * @return instance of {@link WebUserTrackingTelemetryModule}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebUserTrackingTelemetryModule.enabled", havingValue = "true", matchIfMissing = true)
+ public WebUserTrackingTelemetryModule webUserTrackingTelemetryModule() {
+ return new WebUserTrackingTelemetryModule();
+ }
+
+ /**
+ * Bean for WebPerformanceCounterModule
+ * @return instance of {@link WebPerformanceCounterModule}
+ */
+ @Bean
+ @DependsOn("performanceCounterContainer")
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebPerformanceCounterModule.enabled", havingValue = "true", matchIfMissing = true)
+ public WebPerformanceCounterModule webPerformanceCounterModule() {
+ return new WebPerformanceCounterModule();
+ }
+
+ /**
+ * Bean for WebOperationIdTelemetryInitializer
+ * @return instance of {@link WebOperationIdTelemetryInitializer}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebOperationIdTelemetryInitializer.enabled", havingValue = "true", matchIfMissing = true)
+ public WebOperationIdTelemetryInitializer webOperationIdTelemetryInitializer() {
+ return new WebOperationIdTelemetryInitializer();
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebOperationNameTelemetryInitializer.enabled", havingValue = "true", matchIfMissing = true)
+ public WebOperationNameTelemetryInitializer webOperationNameTelemetryInitializer() {
+ return new WebOperationNameTelemetryInitializer();
+ }
+
+ /**
+ * Bean for WebSessionTelemetryInitializer
+ * @return instance of {@link WebSessionTelemetryInitializer}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebSessionTelemetryInitializer.enabled", havingValue = "true", matchIfMissing = true)
+ public WebSessionTelemetryInitializer webSessionTelemetryInitializer() {
+ return new WebSessionTelemetryInitializer();
+ }
+
+ /**
+ * Bean for WebUserTelemetryInitializer
+ * @return instance of {@link WebUserTelemetryInitializer}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebUserTelemetryInitializer.enabled", havingValue = "true", matchIfMissing = true)
+ public WebUserTelemetryInitializer webUserTelemetryInitializer() {
+ return new WebUserTelemetryInitializer();
+ }
+
+ /**
+ * Bean for WebUserAgentTelemetryInitializer
+ * @return instance of {@link WebUserAgentTelemetryInitializer}
+ */
+ @Bean
+ @ConditionalOnProperty(value = "azure.application-insights.default-modules.WebUserAgentTelemetryInitializer.enabled", havingValue = "true", matchIfMissing = true)
+ public WebUserAgentTelemetryInitializer webUserAgentTelemetryInitializer() {
+ return new WebUserAgentTelemetryInitializer();
+ }
+}
diff --git a/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebMvcAutoConfiguration.java b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebMvcAutoConfiguration.java
new file mode 100644
index 00000000000..81ecb292fca
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebMvcAutoConfiguration.java
@@ -0,0 +1,75 @@
+/*
+ * ApplicationInsights-Java
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the ""Software""), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package com.microsoft.applicationinsights.boot;
+
+import com.microsoft.applicationinsights.TelemetryConfiguration;
+import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter;
+import com.microsoft.applicationinsights.web.spring.internal.InterceptorRegistry;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.Ordered;
+
+/**
+ * Configuration for Auto-collection of HTTP requests.
+ *
+ *
+ * This class is responsible for configuring {@link WebRequestTrackingFilter} for auto collection
+ * of incoming HTTP requests
+ *
+ *
+ * @author Arthur Gavlyukovskiy
+ */
+
+@Configuration
+@Import(InterceptorRegistry.class)
+@ConditionalOnBean(TelemetryConfiguration.class)
+@ConditionalOnWebApplication
+@ConditionalOnProperty(value = "azure.application-insights.web.enabled", havingValue = "true", matchIfMissing = true)
+@AutoConfigureAfter(ApplicationInsightsTelemetryAutoConfiguration.class)
+public class ApplicationInsightsWebMvcAutoConfiguration {
+
+ @Bean
+ public FilterRegistrationBean webRequestTrackingFilterRegistrationBean(WebRequestTrackingFilter webRequestTrackingFilter) {
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ registration.setFilter(webRequestTrackingFilter);
+ registration.addUrlPatterns("/*");
+ registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
+ return registration;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @DependsOn("telemetryConfiguration")
+ public WebRequestTrackingFilter webRequestTrackingFilter(@Value("${spring.application.name:application}") String applicationName) {
+ return new WebRequestTrackingFilter(applicationName);
+ }
+}
+
diff --git a/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/HeartBeatProvider/SpringBootHeartBeatProvider.java b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/HeartBeatProvider/SpringBootHeartBeatProvider.java
new file mode 100644
index 00000000000..feeb194440a
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/HeartBeatProvider/SpringBootHeartBeatProvider.java
@@ -0,0 +1,142 @@
+package com.microsoft.applicationinsights.boot.HeartBeatProvider;
+
+import com.microsoft.applicationinsights.internal.heartbeat.HeartBeatPayloadProviderInterface;
+import com.microsoft.applicationinsights.internal.heartbeat.HeartBeatProviderInterface;
+import com.microsoft.applicationinsights.internal.heartbeat.MiscUtils;
+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.concurrent.Callable;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.springframework.boot.SpringBootVersion;
+import org.springframework.core.SpringVersion;
+import org.springframework.core.env.Environment;
+
+/**
+ * SpringBoot Heartbeat Property Provider
+ *
+ * This class is a concrete implementation of {@link HeartBeatPayloadProviderInterface}
+ * It enables setting SpringBoot Metadata to heartbeat payload.
+ *
+ *
+ * @author Dhaval Doshi
+ */
+public class SpringBootHeartBeatProvider implements HeartBeatPayloadProviderInterface {
+
+ /**
+ * Collection holding default properties for this default provider.
+ */
+ private final Set defaultFields;
+
+ /**
+ * Name of this provider.
+ */
+ private final String name = "SpringBootProvider";
+
+ private final Environment environment;
+
+ private final String SPRING_BOOT_VERSION = "ai.spring-boot.version";
+
+ private final String SPRING_VERSION = "ai.spring.version";
+
+ private final String SPRING_BOOT_STARTER_VERSION = "ai.spring.boot.starter.version";
+
+
+
+ public SpringBootHeartBeatProvider(Environment environment) {
+ defaultFields = new HashSet<>();
+ this.environment = environment;
+ initializeDefaultFields(defaultFields);
+ }
+
+ @Override
+
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public boolean isKeyword(String keyword) {
+ return defaultFields.contains(keyword);
+ }
+
+ @Override
+ public Callable setDefaultPayload(final List disableFields,
+ final HeartBeatProviderInterface provider) {
+ return new Callable() {
+
+ Set enabledProperties = MiscUtils.except(disableFields, defaultFields);
+ @Override
+ public Boolean call() {
+ boolean hasSetValues = false;
+ for (String fieldName : enabledProperties) {
+ try {
+ switch (fieldName) {
+ case SPRING_BOOT_VERSION:
+ provider.addHeartBeatProperty(fieldName, getSpringBootVersion(), true);
+ hasSetValues = true;
+ break;
+ case SPRING_VERSION:
+ provider.addHeartBeatProperty(fieldName, getSpringVersion(), true);
+ hasSetValues = true;
+ break;
+ case SPRING_BOOT_STARTER_VERSION:
+ provider.addHeartBeatProperty(fieldName, getSpringBootStarterVersionNumber(), true);
+ 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 defaultFields) {
+ defaultFields.add(SPRING_BOOT_VERSION);
+ defaultFields.add(SPRING_VERSION);
+ defaultFields.add(SPRING_BOOT_STARTER_VERSION);
+ }
+
+ /**
+ * Gets the version of SpringBoot
+ * @return returns springboot version string
+ */
+ private String getSpringBootVersion() {
+ return SpringBootVersion.getVersion();
+ }
+
+ /**
+ * Gets the Spring Framework version
+ * @return the SpringFrameWork version String
+ */
+ private String getSpringVersion() {
+ return SpringVersion.getVersion();
+ }
+
+ /**
+ * Gets the AI SpringBoot starter version number
+ * @return the AI SpringBoot starter version number
+ */
+ private String getSpringBootStarterVersionNumber() {
+ Properties starterVersionProperties = PropertyHelper.getStarterVersionProperties();
+ if (starterVersionProperties != null) {
+ return starterVersionProperties.getProperty("version");
+ }
+ return "undefined";
+ }
+
+}
diff --git a/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/initializer/SpringBootTelemetryInitializer.java b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/initializer/SpringBootTelemetryInitializer.java
new file mode 100644
index 00000000000..930d91b27e2
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/java/com/microsoft/applicationinsights/boot/initializer/SpringBootTelemetryInitializer.java
@@ -0,0 +1,31 @@
+package com.microsoft.applicationinsights.boot.initializer;
+
+import com.microsoft.applicationinsights.extensibility.TelemetryInitializer;
+import com.microsoft.applicationinsights.extensibility.context.DeviceContext;
+import com.microsoft.applicationinsights.telemetry.Telemetry;
+import org.springframework.beans.factory.annotation.Value;
+
+/**
+ * TelemetryInitializer to set the CloudRoleName Instance
+ *
+ *
+ * This Telemetry Initializer is used to auto-configure cloud_RoleName field
+ * to get a logical component on AppMap.
+ *
+ *
+ * @author Dhaval Doshi
+ */
+public class SpringBootTelemetryInitializer implements TelemetryInitializer {
+
+ /** The Logical Name of SpringBoot Application*/
+ @Value("${spring.application.name:application}")
+ String appName;
+
+ @Override
+ public void initialize(Telemetry telemetry) {
+
+ DeviceContext device = telemetry.getContext().getDevice();
+ device.setRoleName(appName);
+ }
+
+}
diff --git a/azure-application-insights-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/azure-application-insights-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100644
index 00000000000..923d5a1e734
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -0,0 +1,70 @@
+{
+ "properties": [
+ {
+ "name": "azure.application-insights.default-modules.ProcessPerformanceCountersModule.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable ProcessPerformanceCountersModule.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebRequestTrackingTelemetryModule.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebRequestTrackingTelemetryModule.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebSessionTrackingTelemetryModule.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebSessionTrackingTelemetryModule.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebUserTrackingTelemetryModule.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebUserTrackingTelemetryModule.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebPerformanceCounterModule.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebPerformanceCounterModule.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebOperationIdTelemetryInitializer.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebOperationIdTelemetryInitializer.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebOperationNameTelemetryInitializer.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebOperationNameTelemetryInitializer.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebSessionTelemetryInitializer.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebSessionTelemetryInitializer.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebUserTelemetryInitializer.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebUserTelemetryInitializer.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default-modules.WebUserAgentTelemetryInitializer.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable WebUserAgentTelemetryInitializer.",
+ "defaultValue": "true"
+ },
+ {
+ "name": "azure.application-insights.default.modules.JvmPerformanceCountersModule.enabled",
+ "type":"java.lang.Boolean",
+ "description": "Enable JvmPerformanceCountersModule.",
+ "defaultValue": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/azure-application-insights-spring-boot-starter/src/main/resources/META-INF/spring.factories b/azure-application-insights-spring-boot-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000000..bfbc19b901b
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,22 @@
+# ApplicationInsights-Java
+# Copyright (c) Microsoft Corporation
+# All rights reserved.
+#
+# MIT License
+# Permission is hereby granted, free of charge, to any person obtaining a copy of this
+# software and associated documentation files (the ""Software""), to deal in the Software
+# without restriction, including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the following conditions:
+# The above copyright notice and this permission notice shall be included in all copies or
+# substantial portions of the Software.
+# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.microsoft.applicationinsights.boot.ApplicationInsightsTelemetryAutoConfiguration,\
+com.microsoft.applicationinsights.boot.ApplicationInsightsWebMvcAutoConfiguration
\ No newline at end of file
diff --git a/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsStarterCoreParityTests.java b/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsStarterCoreParityTests.java
new file mode 100644
index 00000000000..cb74ddb5c0b
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsStarterCoreParityTests.java
@@ -0,0 +1,75 @@
+package com.microsoft.applicationinsights.boot;
+
+import com.microsoft.applicationinsights.TelemetryClient;
+import com.microsoft.applicationinsights.TelemetryConfiguration;
+import com.microsoft.applicationinsights.internal.logger.InternalLogger;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@SpringBootTest(
+ properties = {
+ "spring.application.name: test-application",
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000"
+ },
+ classes = {
+ PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class,
+ ApplicationInsightsWebMvcAutoConfiguration.class
+ }
+)
+@RunWith(SpringRunner.class)
+public class ApplicationInsightsStarterCoreParityTests {
+
+ //Instance from Spring Bean Factory
+ @Autowired
+ TelemetryClient telemetryClient;
+
+ @Test
+ public void shouldHaveIdenticalConfiguration() throws Exception{
+ Field field = telemetryClient.getClass().getDeclaredField("configuration");
+ field.setAccessible(true);
+ TelemetryConfiguration config1 = (TelemetryConfiguration)field.get(telemetryClient);
+
+ //needed for clearing down the active instance and get the new config.
+ tearDown();
+
+ //Instance created from XML config.
+ TelemetryClient t2 = new TelemetryClient();
+
+ Field field2 = t2.getClass().getDeclaredField("configuration");
+ field2.setAccessible(true);
+ TelemetryConfiguration config2 = (TelemetryConfiguration)field2.get(t2);
+
+ Assert.assertNotEquals(config1, config2);
+ //There is one additional TelemetryInitializer in case of SpringBoot(For Cloud_RoleName)
+ Assert.assertEquals(config1.getTelemetryInitializers().size(), config2.getTelemetryInitializers().size() + 1);
+ Assert.assertEquals(config1.getTelemetryModules().size(), config2.getTelemetryModules().size());
+ Assert.assertEquals(config1.getContextInitializers().size(), config2.getContextInitializers().size());
+ Assert.assertEquals(config1.getTelemetryProcessors().size(), config2.getTelemetryProcessors().size());
+ Assert.assertEquals(config1.getInstrumentationKey(), config2.getInstrumentationKey());
+ Assert.assertEquals(config1.isTrackingDisabled(), config2.isTrackingDisabled());
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ Method method = TelemetryConfiguration.class.getDeclaredMethod("setActiveAsNull");
+ method.setAccessible(true);
+ method.invoke(null);
+
+ //InternalLogger needs to be shutdown
+ Field field = InternalLogger.class.getDeclaredField("initialized");
+ field.setAccessible(true);
+ field.set(InternalLogger.INSTANCE, false);
+ System.out.println("Exiting core parity tests");
+ }
+
+
+}
diff --git a/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsTelemetryAutoConfigurationTests.java b/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsTelemetryAutoConfigurationTests.java
new file mode 100644
index 00000000000..316e9bfc8bb
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsTelemetryAutoConfigurationTests.java
@@ -0,0 +1,330 @@
+/*
+ * ApplicationInsights-Java
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the ""Software""), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package com.microsoft.applicationinsights.boot;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.microsoft.applicationinsights.TelemetryClient;
+import com.microsoft.applicationinsights.TelemetryConfiguration;
+import com.microsoft.applicationinsights.channel.TelemetryChannel;
+import com.microsoft.applicationinsights.channel.concrete.inprocess.InProcessTelemetryChannel;
+import com.microsoft.applicationinsights.extensibility.ContextInitializer;
+import com.microsoft.applicationinsights.extensibility.TelemetryInitializer;
+import com.microsoft.applicationinsights.extensibility.TelemetryModule;
+import com.microsoft.applicationinsights.extensibility.TelemetryProcessor;
+import com.microsoft.applicationinsights.internal.channel.samplingV2.FixedRateSamplingTelemetryProcessor;
+import com.microsoft.applicationinsights.internal.heartbeat.HeartBeatModule;
+import com.microsoft.applicationinsights.internal.logger.InternalLogger;
+import com.microsoft.applicationinsights.internal.perfcounter.JvmPerformanceCountersModule;
+import com.microsoft.applicationinsights.internal.perfcounter.PerformanceCounter;
+import com.microsoft.applicationinsights.internal.perfcounter.PerformanceCounterContainer;
+import com.microsoft.applicationinsights.telemetry.EventTelemetry;
+import com.microsoft.applicationinsights.telemetry.RequestTelemetry;
+import com.microsoft.applicationinsights.telemetry.Telemetry;
+import com.microsoft.applicationinsights.telemetry.TelemetryContext;
+import com.microsoft.applicationinsights.web.extensibility.modules.WebUserTrackingTelemetryModule;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.test.util.EnvironmentTestUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * @author Arthur Gavlyukovskiy, Dhaval Doshi
+ */
+public final class ApplicationInsightsTelemetryAutoConfigurationTests {
+
+ private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+
+ @After
+ public void restore() {
+ context.close();
+ }
+
+ @Test
+ public void shouldSetInstrumentationKeyWhenContextLoads() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ TelemetryClient telemetryClient = context.getBean(TelemetryClient.class);
+ TelemetryConfiguration telemetryConfiguration = context.getBean(TelemetryConfiguration.class);
+
+ assertThat(telemetryConfiguration).isSameAs(TelemetryConfiguration.getActive());
+ assertThat(telemetryConfiguration.getInstrumentationKey()).isEqualTo("00000000-0000-0000-0000-000000000000");
+ assertThat(telemetryClient.getContext().getInstrumentationKey()).isEqualTo("00000000-0000-0000-0000-000000000000");
+ }
+
+ @Test
+ public void shouldSetInstrumentationKeyFromRelaxedCase() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "AZURE.APPLICATION_INSIGHTS.INSTRUMENTATION_KEY: 00000000-0000-0000-0000-000000000000");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ TelemetryClient telemetryClient = context.getBean(TelemetryClient.class);
+ TelemetryConfiguration telemetryConfiguration = context.getBean(TelemetryConfiguration.class);
+
+ assertThat(telemetryConfiguration).isSameAs(TelemetryConfiguration.getActive());
+ assertThat(telemetryConfiguration.getInstrumentationKey()).isEqualTo("00000000-0000-0000-0000-000000000000");
+ assertThat(telemetryClient.getContext().getInstrumentationKey()).isEqualTo("00000000-0000-0000-0000-000000000000");
+ }
+
+ @Test
+ @Ignore
+ public void shouldReloadInstrumentationKeyOnTelemetryClient() {
+ TelemetryClient myClient = new TelemetryClient();
+
+ EventTelemetry eventTelemetry1 = new EventTelemetry("test1");
+ myClient.trackEvent(eventTelemetry1);
+ assertThat(eventTelemetry1.getTimestamp()).isNull();
+
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ EventTelemetry eventTelemetry2 = new EventTelemetry("test2");
+ myClient.trackEvent(eventTelemetry2);
+ assertThat(eventTelemetry2.getTimestamp()).describedAs("Expecting telemetry event to be sent").isNotNull();
+ }
+
+ @Test
+ public void shouldNotFailIfInstrumentationKeyIsNotSet() {
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ assertThat(context.getBeansOfType(TelemetryClient.class)).isEmpty();
+ assertThat(context.getBeansOfType(TelemetryConfiguration.class)).isEmpty();
+ }
+
+ @Test
+ public void shouldBeAbleToDisableInstrumentationByProperty() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.enabled: false",
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ TelemetryConfiguration telemetryConfiguration = context.getBean(TelemetryConfiguration.class);
+ assertThat(telemetryConfiguration.isTrackingDisabled()).isTrue();
+ }
+
+ @Test
+ public void shouldBeAbleToConfigureTelemetryChannel() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.channel.in-process.developer-mode=false",
+ "azure.application-insights.channel.in-process.flush-interval-in-seconds=123",
+ "azure.application-insights.channel.in-process.max-telemetry-buffer-capacity=10");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ TelemetryConfiguration telemetryConfiguration = context.getBean(TelemetryConfiguration.class);
+ TelemetryChannel channel = telemetryConfiguration.getChannel();
+
+ assertThat(channel).isInstanceOf(InProcessTelemetryChannel.class);
+ assertThat(channel.isDeveloperMode()).isFalse();
+ assertThat(channel).extracting("telemetryBuffer").extracting("transmitBufferTimeoutInSeconds").contains(123);
+ assertThat(channel).extracting("telemetryBuffer").extracting("maxTelemetriesInBatch").contains(10);
+ }
+
+ @Test
+ public void shouldBeAbleToConfigureSamplingTelemetryProcessor() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.telemetry-processor.sampling.percentage=50",
+ "azure.application-insights.telemetry-processor.sampling.include=Request",
+ "azure.application-insights.telemetry-processor.sampling.enabled=true");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ TelemetryConfiguration telemetryConfiguration = context.getBean(TelemetryConfiguration.class);
+ FixedRateSamplingTelemetryProcessor fixedRateSamplingTelemetryProcessor = context.getBean(FixedRateSamplingTelemetryProcessor.class);
+
+ assertThat(telemetryConfiguration.getTelemetryProcessors()).extracting("class").contains(FixedRateSamplingTelemetryProcessor.class);
+ assertThat(fixedRateSamplingTelemetryProcessor).extracting("samplingPercentage").contains(50.);
+ assertThat(fixedRateSamplingTelemetryProcessor.getIncludedTypes()).contains(RequestTelemetry.class);
+ assertThat(fixedRateSamplingTelemetryProcessor.getExcludedTypes()).isEmpty();
+ }
+
+ @Test
+ public void shouldBeAbleToDisableAllWebModules() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.web.enabled=false");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ assertThat(context.getBeansOfType(WebUserTrackingTelemetryModule.class)).isEmpty();
+ }
+
+ @Test
+ public void internalLoggerShouldBeInitializedBeforeTelemetryConfiguration() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.logger.level=INFO"
+ );
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+ InternalLogger logger = context.getBean(InternalLogger.class);
+ assertThat(logger.isInfoEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void shouldBeAbleToDisableDefaultModules() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.default-modules.WebUserTrackingTelemetryModule.enabled=false");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ assertThat(context.getBeansOfType(WebUserTrackingTelemetryModule.class)).isEmpty();
+ }
+
+ @Test
+ public void shouldBeAbleToAddCustomModules() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class,
+ CustomModuleConfiguration.class);
+ context.refresh();
+
+ TelemetryConfiguration telemetryConfiguration = context.getBean(TelemetryConfiguration.class);
+
+ ContextInitializer myContextInitializer = context.getBean("myContextInitializer", ContextInitializer.class);
+ TelemetryInitializer myTelemetryInitializer = context.getBean("myTelemetryInitializer", TelemetryInitializer.class);
+ TelemetryModule myTelemetryModule = context.getBean("myTelemetryModule", TelemetryModule.class);
+ TelemetryProcessor myTelemetryProcessor = context.getBean("myTelemetryProcessor", TelemetryProcessor.class);
+
+ assertThat(telemetryConfiguration.getContextInitializers()).contains(myContextInitializer);
+ assertThat(telemetryConfiguration.getTelemetryInitializers()).contains(myTelemetryInitializer);
+ assertThat(telemetryConfiguration.getTelemetryModules()).contains(myTelemetryModule);
+ assertThat(telemetryConfiguration.getTelemetryProcessors()).contains(myTelemetryProcessor);
+ }
+
+ @Test
+ public void shouldBeAbleToConfigureJmxPerformanceCounters() throws Exception{
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.jmx.jmx-counters:"
+ + "java.lang:type=ClassLoading/LoadedClassCount/Current Loaded Class Count");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ PerformanceCounterContainer counterContainer = context.getBean(PerformanceCounterContainer.class);
+ Field field = counterContainer.getClass().getDeclaredField("performanceCounters");
+ field.setAccessible(true);
+ Map map = (Map)field.get(counterContainer);
+ assertThat(map.containsKey("java.lang:type=ClassLoading")).isNotNull();
+ }
+
+ @Test
+ public void shouldBeAbleToConfigureJvmPerformanceCounters() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.default.modules.JvmPerformanceCountersModule.enabled=true");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ assertThat(context.getBeansOfType(JvmPerformanceCountersModule.class)).isNotEmpty();
+ }
+
+ @Test
+ public void heartBeatModuleShouldBeEnabledByDefault() {
+ EnvironmentTestUtils.addEnvironment(context,
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000",
+ "azure.application-insights.heart-beat.enabled=true");
+ context.register(PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class);
+ context.refresh();
+
+ assertThat(context.getBeansOfType(HeartBeatModule.class)).isNotEmpty();
+ }
+
+ private static class CustomModuleConfiguration {
+
+ @Bean
+ public ContextInitializer myContextInitializer() {
+ return new ContextInitializer() {
+ @Override
+ public void initialize(TelemetryContext context) {
+ }
+ };
+ }
+
+ @Bean
+ public TelemetryInitializer myTelemetryInitializer() {
+ return new TelemetryInitializer() {
+ @Override
+ public void initialize(Telemetry telemetry) {
+ }
+ };
+ }
+
+ @Bean
+ public TelemetryModule myTelemetryModule() {
+ return new TelemetryModule() {
+ @Override
+ public void initialize(TelemetryConfiguration configuration) {
+ }
+ };
+ }
+
+ @Bean
+ public TelemetryProcessor myTelemetryProcessor() {
+ return new TelemetryProcessor() {
+ @Override
+ public boolean process(Telemetry telemetry) {
+ return true;
+ }
+ };
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ Method method = TelemetryConfiguration.class.getDeclaredMethod("setActiveAsNull");
+ method.setAccessible(true);
+ method.invoke(null);
+ }
+}
\ No newline at end of file
diff --git a/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebMvcAutoConfigurationTests.java b/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebMvcAutoConfigurationTests.java
new file mode 100644
index 00000000000..51e30786fd5
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/test/java/com/microsoft/applicationinsights/boot/ApplicationInsightsWebMvcAutoConfigurationTests.java
@@ -0,0 +1,79 @@
+/*
+ * ApplicationInsights-Java
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the ""Software""), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package com.microsoft.applicationinsights.boot;
+
+import com.microsoft.applicationinsights.internal.quickpulse.QuickPulse;
+import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
+import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Arthur Gavlyukovskiy
+ */
+@SpringBootTest(
+ properties = {
+ "spring.test.mockmvc: true",
+ "spring.application.name: test-application",
+ "azure.application-insights.instrumentation-key: 00000000-0000-0000-0000-000000000000"
+ },
+ classes = {
+ EmbeddedServletContainerAutoConfiguration.class,
+ ServerPropertiesAutoConfiguration.class,
+ DispatcherServletAutoConfiguration.class,
+ HttpMessageConvertersAutoConfiguration.class,
+ WebMvcAutoConfiguration.class,
+ MockMvcAutoConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class,
+ ApplicationInsightsTelemetryAutoConfiguration.class,
+ ApplicationInsightsWebMvcAutoConfiguration.class
+ },
+ webEnvironment = WebEnvironment.RANDOM_PORT
+)
+@RunWith(SpringRunner.class)
+public class ApplicationInsightsWebMvcAutoConfigurationTests {
+
+ @Autowired
+ private ApplicationContext context;
+
+ @Test
+ public void shouldRegisterWebRequestTrackingFilter() {
+ WebRequestTrackingFilter webRequestTrackingFilter = context.getBean(WebRequestTrackingFilter.class);
+
+ assertThat(webRequestTrackingFilter).extracting("appName").contains("test-application");
+ assertThat(QuickPulse.INSTANCE).extracting("initialized").contains(true);
+ }
+
+}
\ No newline at end of file
diff --git a/azure-application-insights-spring-boot-starter/src/test/resources/ApplicationInsights.xml b/azure-application-insights-spring-boot-starter/src/test/resources/ApplicationInsights.xml
new file mode 100644
index 00000000000..5c85a0210c7
--- /dev/null
+++ b/azure-application-insights-spring-boot-starter/src/test/resources/ApplicationInsights.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+ 00000000-0000-0000-0000-000000000000
+
+
+ true
+ JavaSDKLog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/main/java/com/microsoft/applicationinsights/TelemetryConfiguration.java b/core/src/main/java/com/microsoft/applicationinsights/TelemetryConfiguration.java
index 5c505726357..3625e9bb023 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/TelemetryConfiguration.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/TelemetryConfiguration.java
@@ -74,6 +74,24 @@ public static TelemetryConfiguration getActive() {
return active;
}
+ /**
+ * This method provides the new instance of TelmetryConfiguration without loading the configuration
+ * from configuration file. This will just give a plain bare bone instance. Typically used when
+ * performing configuration programatically by creating beans, using @Beans tags. This is a common
+ * scenario in SpringBoot.
+ * @return {@link com.microsoft.applicationinsights.TelemetryConfiguration}
+ */
+ public static TelemetryConfiguration getActiveWithoutInitializingConfig() {
+ if (active == null) {
+ synchronized (s_lock) {
+ if (active == null) {
+ active = new TelemetryConfiguration();
+ }
+ }
+ }
+ return active;
+ }
+
/**
* Creates a new instance loaded from the ApplicationInsights.xml file.
* If the configuration file does not exist, the new configuration instance is initialized with minimum defaults
diff --git a/core/src/main/java/com/microsoft/applicationinsights/channel/concrete/inprocess/InProcessTelemetryChannel.java b/core/src/main/java/com/microsoft/applicationinsights/channel/concrete/inprocess/InProcessTelemetryChannel.java
index 4fcd89af4cc..aa375c3ea02 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/channel/concrete/inprocess/InProcessTelemetryChannel.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/channel/concrete/inprocess/InProcessTelemetryChannel.java
@@ -21,15 +21,11 @@
package com.microsoft.applicationinsights.channel.concrete.inprocess;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.net.URI;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-import com.microsoft.applicationinsights.internal.channel.TelemetriesTransmitter;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.microsoft.applicationinsights.channel.TelemetryChannel;
import com.microsoft.applicationinsights.channel.TelemetrySampler;
+import com.microsoft.applicationinsights.internal.channel.TelemetriesTransmitter;
import com.microsoft.applicationinsights.internal.channel.TransmitterFactory;
import com.microsoft.applicationinsights.internal.channel.common.TelemetryBuffer;
import com.microsoft.applicationinsights.internal.logger.InternalLogger;
@@ -38,48 +34,47 @@
import com.microsoft.applicationinsights.internal.util.Sanitizer;
import com.microsoft.applicationinsights.telemetry.JsonTelemetryDataSerializer;
import com.microsoft.applicationinsights.telemetry.Telemetry;
-import com.microsoft.applicationinsights.channel.TelemetryChannel;
-
-import com.google.common.base.Strings;
-import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.exception.ExceptionUtils;
/**
- * An implementation of
- * {@link com.microsoft.applicationinsights.channel.TelemetryChannel}
- *
- * The channel holds two main entities:
- *
- * A buffer for incoming
- * {@link com.microsoft.applicationinsights.telemetry.Telemetry} instances A
- * transmitter
- *
- * The buffer is stores incoming telemetry instances. Every new buffer starts a
- * timer. When the timer expires, or when the buffer is 'full' (whichever
- * happens first), the transmitter will pick up that buffer and will handle its
- * sending to the server. For example, a transmitter will be responsible for
- * compressing, sending and activate a policy in case of failures.
- *
- * The model here is:
- *
- * Use application threads to populate the buffer Use channel's threads to send
- * buffers to the server
- *
- * Created by gupele on 12/17/2014.
+ * An implementation of {@link com.microsoft.applicationinsights.channel.TelemetryChannel}
+ *
+ *
The channel holds two main entities:
+ *
+ *
A buffer for incoming {@link com.microsoft.applicationinsights.telemetry.Telemetry} instances
+ * A transmitter
+ *
+ *
The buffer is stores incoming telemetry instances. Every new buffer starts a timer. When the
+ * timer expires, or when the buffer is 'full' (whichever happens first), the transmitter will pick
+ * up that buffer and will handle its sending to the server. For example, a transmitter will be
+ * responsible for compressing, sending and activate a policy in case of failures.
+ *
+ *
The model here is:
+ *
+ *
Use application threads to populate the buffer Use channel's threads to send buffers to the
+ * server
+ *
+ *
Created by gupele on 12/17/2014.
*/
public final class InProcessTelemetryChannel implements TelemetryChannel {
-
- private final static String INSTANT_RETRY_NAME = "MaxInstantRetry";
- private final static int DEFAULT_MAX_INSTANT_RETRY = 3;
- private final static int DEFAULT_MAX_TELEMETRY_BUFFER_CAPACITY = 500;
- private final static int MIN_MAX_TELEMETRY_BUFFER_CAPACITY = 1;
- private final static int MAX_MAX_TELEMETRY_BUFFER_CAPACITY = 1000;
- private final static String MAX_MAX_TELEMETRY_BUFFER_CAPACITY_NAME = "MaxTelemetryBufferCapacity";
-
- private final static int DEFAULT_FLUSH_BUFFER_TIMEOUT_IN_SECONDS = 5;
- private final static int MIN_FLUSH_BUFFER_TIMEOUT_IN_SECONDS = 1;
- private final static int MAX_FLUSH_BUFFER_TIMEOUT_IN_SECONDS = 300;
- private final static String FLUSH_BUFFER_TIMEOUT_IN_SECONDS_NAME = "FlushIntervalInSeconds";
+
+ private static final String INSTANT_RETRY_NAME = "MaxInstantRetry";
+ public static final int DEFAULT_MAX_INSTANT_RETRY = 3;
+ public static final int DEFAULT_MAX_TELEMETRY_BUFFER_CAPACITY = 500;
+ private static final int MIN_MAX_TELEMETRY_BUFFER_CAPACITY = 1;
+ private static final int MAX_MAX_TELEMETRY_BUFFER_CAPACITY = 1000;
+ private static final String MAX_MAX_TELEMETRY_BUFFER_CAPACITY_NAME = "MaxTelemetryBufferCapacity";
+
+ public static final int DEFAULT_FLUSH_BUFFER_TIMEOUT_IN_SECONDS = 5;
+ private static final int MIN_FLUSH_BUFFER_TIMEOUT_IN_SECONDS = 1;
+ private static final int MAX_FLUSH_BUFFER_TIMEOUT_IN_SECONDS = 300;
+ private static final String FLUSH_BUFFER_TIMEOUT_IN_SECONDS_NAME = "FlushIntervalInSeconds";
private final static String DEVELOPER_MODE_SYSTEM_PROPRETY_NAME = "APPLICATION_INSIGHTS_DEVELOPER_MODE";
@@ -114,31 +109,52 @@ public InProcessTelemetryChannel() {
createDefaultSendIntervalInSecondsEnforcer(null), true);
}
- /**
- * Ctor
- *
- * @param endpointAddress
- * Must be empty string or a valid uri, else an exception will be
- * thrown
- * @param developerMode
- * True will behave in a 'non-production' mode to ease the debugging
- * @param maxTelemetryBufferCapacity
- * Max number of Telemetries we keep in the buffer, when reached we
- * will send the buffer Note, value should be between
- * TRANSMIT_BUFFER_MIN_TIMEOUT_IN_MILLIS and
- * TRANSMIT_BUFFER_MAX_TIMEOUT_IN_MILLIS inclusive
- * @param sendIntervalInMillis
- * The maximum number of milliseconds to wait before we send the
- * buffer Note, value should be between
- * MIN_MAX_TELEMETRY_BUFFER_CAPACITY and
- * MAX_MAX_TELEMETRY_BUFFER_CAPACITY inclusive
- */
- public InProcessTelemetryChannel(String endpointAddress, boolean developerMode, int maxTelemetryBufferCapacity,
- int sendIntervalInMillis) {
- initialize(endpointAddress, null, developerMode,
- createDefaultMaxTelemetryBufferCapacityEnforcer(maxTelemetryBufferCapacity),
- createDefaultSendIntervalInSecondsEnforcer(sendIntervalInMillis), true);
- }
+ /**
+ * Ctor
+ *
+ * @param endpointAddress Must be empty string or a valid uri, else an exception will be thrown
+ * @param developerMode True will behave in a 'non-production' mode to ease the debugging
+ * @param maxTelemetryBufferCapacity Max number of Telemetries we keep in the buffer, when reached
+ * we will send the buffer Note, value should be between TRANSMIT_BUFFER_MIN_TIMEOUT_IN_MILLIS
+ * and TRANSMIT_BUFFER_MAX_TIMEOUT_IN_MILLIS inclusive
+ * @param sendIntervalInMillis The maximum number of milliseconds to wait before we send the
+ * buffer Note, value should be between MIN_MAX_TELEMETRY_BUFFER_CAPACITY and
+ * MAX_MAX_TELEMETRY_BUFFER_CAPACITY inclusive
+ */
+ public InProcessTelemetryChannel(
+ String endpointAddress,
+ boolean developerMode,
+ int maxTelemetryBufferCapacity,
+ int sendIntervalInMillis) {
+
+ // Builder pattern implementation
+ this(
+ endpointAddress,
+ null,
+ developerMode,
+ maxTelemetryBufferCapacity,
+ sendIntervalInMillis,
+ true,
+ DEFAULT_MAX_INSTANT_RETRY);
+ }
+
+ public InProcessTelemetryChannel(
+ String endpointAddress,
+ String maxTransmissionStorageCapacity,
+ boolean developerMode,
+ int maxTelemetryBufferCapacity,
+ int sendIntervalInMillis,
+ boolean throttling,
+ int maxInstantRetries) {
+ initialize(
+ endpointAddress,
+ maxTransmissionStorageCapacity,
+ developerMode,
+ createDefaultMaxTelemetryBufferCapacityEnforcer(maxTelemetryBufferCapacity),
+ createDefaultSendIntervalInSecondsEnforcer(sendIntervalInMillis),
+ throttling,
+ maxInstantRetries);
+ }
/**
* This Ctor will query the 'namesAndValues' map for data to initialize itself
@@ -152,7 +168,7 @@ public InProcessTelemetryChannel(Map namesAndValues) {
boolean developerMode = false;
String endpointAddress = null;
int maxInstantRetries = DEFAULT_MAX_INSTANT_RETRY;
-
+
LimitsEnforcer maxTelemetryBufferCapacityEnforcer = createDefaultMaxTelemetryBufferCapacityEnforcer(null);
LimitsEnforcer sendIntervalInSecondsEnforcer = createDefaultSendIntervalInSecondsEnforcer(null);
@@ -164,17 +180,17 @@ public InProcessTelemetryChannel(Map namesAndValues) {
try {
String instantRetryValue = namesAndValues.get(INSTANT_RETRY_NAME);
if (instantRetryValue != null){
- maxInstantRetries = Integer.parseInt(instantRetryValue);
+ maxInstantRetries = Integer.parseInt(instantRetryValue);
}
-
+
} catch (NumberFormatException e) {
InternalLogger.INSTANCE.error("Unable to parse configuration setting %s to integer value.%nStack Trace:%n%s", INSTANT_RETRY_NAME, ExceptionUtils.getStackTrace(e));
}
-
- if (!developerMode) {
- developerMode = Boolean.valueOf(System.getProperty(DEVELOPER_MODE_SYSTEM_PROPRETY_NAME));
- }
- endpointAddress = namesAndValues.get(ENDPOINT_ADDRESS_NAME);
+
+ if (!developerMode) {
+ developerMode = Boolean.valueOf(System.getProperty(DEVELOPER_MODE_SYSTEM_PROPRETY_NAME));
+ }
+ endpointAddress = namesAndValues.get(ENDPOINT_ADDRESS_NAME);
maxTelemetryBufferCapacityEnforcer
.normalizeStringValue(namesAndValues.get(MAX_MAX_TELEMETRY_BUFFER_CAPACITY_NAME));
@@ -182,10 +198,17 @@ public InProcessTelemetryChannel(Map namesAndValues) {
.normalizeStringValue(namesAndValues.get(FLUSH_BUFFER_TIMEOUT_IN_SECONDS_NAME));
}
- String maxTransmissionStorageCapacity = namesAndValues.get(MAX_TRANSMISSION_STORAGE_CAPACITY_NAME);
- initialize(endpointAddress, maxTransmissionStorageCapacity, developerMode, maxTelemetryBufferCapacityEnforcer,
- sendIntervalInSecondsEnforcer, throttling, maxInstantRetries);
- }
+ String maxTransmissionStorageCapacity =
+ namesAndValues.get(MAX_TRANSMISSION_STORAGE_CAPACITY_NAME);
+ initialize(
+ endpointAddress,
+ maxTransmissionStorageCapacity,
+ developerMode,
+ maxTelemetryBufferCapacityEnforcer,
+ sendIntervalInSecondsEnforcer,
+ throttling,
+ maxInstantRetries);
+ }
/**
* Gets value indicating whether this channel is in developer mode.
diff --git a/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionFileSystemOutput.java b/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionFileSystemOutput.java
index 3d4723bab1d..64166a0d08c 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionFileSystemOutput.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionFileSystemOutput.java
@@ -77,7 +77,7 @@ public final class TransmissionFileSystemOutput implements TransmissionOutput {
private final static int MAX_RETRY_FOR_DELETE = 2;
private final static int DELETE_TIMEOUT_ON_FAILURE_IN_MILLS = 100;
- private final static int DEFAULT_CAPACITY_MEGABYTES = 10;
+ public final static int DEFAULT_CAPACITY_MEGABYTES = 10;
private final static int MAX_CAPACITY_MEGABYTES = 1000;
private final static int MIN_CAPACITY_MEGABYTES = 1;
private static final String MAX_TRANSMISSION_STORAGE_CAPACITY_NAME = "Channel.MaxTransmissionStorageCapacityInMB";
diff --git a/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionNetworkOutput.java b/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionNetworkOutput.java
index 311382a5e65..2a80e5a8934 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionNetworkOutput.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/TransmissionNetworkOutput.java
@@ -50,12 +50,12 @@
*
* Created by gupele on 12/18/2014.
*/
-public final class TransmissionNetworkOutput implements TransmissionOutput {
+public final class TransmissionNetworkOutput implements TransmissionOutput {
private final static String CONTENT_TYPE_HEADER = "Content-Type";
private final static String CONTENT_ENCODING_HEADER = "Content-Encoding";
private final static String RESPONSE_THROTTLING_HEADER = "Retry-After";
- private final static String DEFAULT_SERVER_URI = "https://dc.services.visualstudio.com/v2/track";
+ public final static String DEFAULT_SERVER_URI = "https://dc.services.visualstudio.com/v2/track";
// For future use: re-send a failed transmission back to the dispatcher
private TransmissionDispatcher transmissionDispatcher;
@@ -73,7 +73,7 @@ public final class TransmissionNetworkOutput implements TransmissionOutput {
* Creates an instance of the network transmission class.
*
* Will use the DEFAULT_SERVER_URI for the endpoint.
- *
+ *
* @param transmissionPolicyManager
* The transmission policy used to mark this sender active or
* blocked.
@@ -85,7 +85,7 @@ public static TransmissionNetworkOutput create(TransmissionPolicyManager transmi
/**
* Creates an instance of the network transmission class.
- *
+ *
* @param endpoint
* The HTTP endpoint to send our telemetry too.
* @param transmissionPolicyManager
@@ -103,7 +103,7 @@ public static TransmissionNetworkOutput create(String endpoint,
* Private Ctor to initialize class.
*
* Also creates the httpClient using the ApacheSender instance
- *
+ *
* @param serverUri
* The HTTP endpoint to send our telemetry too.
* @param transmissionPolicyManager
@@ -125,7 +125,7 @@ private TransmissionNetworkOutput(String serverUri, TransmissionPolicyManager tr
/**
* Used to inject the dispatcher used for this output so it can be injected to
* the retry logic.
- *
+ *
* @param transmissionDispatcher
* The dispatcher to be injected.
*/
@@ -135,7 +135,7 @@ public void setTransmissionDispatcher(TransmissionDispatcher transmissionDispatc
/**
* Stops all threads from sending data.
- *
+ *
* @param timeout
* The timeout to wait, which is not relevant here.
* @param timeUnit
@@ -157,7 +157,7 @@ public synchronized void stop(long timeout, TimeUnit timeUnit) {
* The thread that calls that method might be suspended if there is a throttling
* issues, in any case the thread that enters this method is responsive for
* 'stop' request that might be issued by the application.
- *
+ *
* @param transmission
* The data to send
* @return True when done.
@@ -259,7 +259,7 @@ public boolean send(Transmission transmission) {
/**
* Generates the HTTP POST to send to the endpoint.
- *
+ *
* @param transmission
* The transmission to send.
* @return The completed {@link HttpPost} object
diff --git a/core/src/main/java/com/microsoft/applicationinsights/internal/channel/samplingV2/FixedRateSamplingTelemetryProcessor.java b/core/src/main/java/com/microsoft/applicationinsights/internal/channel/samplingV2/FixedRateSamplingTelemetryProcessor.java
index a71b2c3d944..48bd2e87dd2 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/internal/channel/samplingV2/FixedRateSamplingTelemetryProcessor.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/internal/channel/samplingV2/FixedRateSamplingTelemetryProcessor.java
@@ -3,8 +3,15 @@
import com.microsoft.applicationinsights.extensibility.TelemetryProcessor;
import com.microsoft.applicationinsights.internal.annotation.BuiltInProcessor;
import com.microsoft.applicationinsights.internal.logger.InternalLogger;
+import com.microsoft.applicationinsights.telemetry.EventTelemetry;
+import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry;
+import com.microsoft.applicationinsights.telemetry.PageViewTelemetry;
+import com.microsoft.applicationinsights.telemetry.RemoteDependencyTelemetry;
+import com.microsoft.applicationinsights.telemetry.RequestTelemetry;
import com.microsoft.applicationinsights.telemetry.SupportSampling;
import com.microsoft.applicationinsights.telemetry.Telemetry;
+import com.microsoft.applicationinsights.telemetry.TraceTelemetry;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import java.util.HashMap;
@@ -12,8 +19,6 @@
import java.util.Map;
import java.util.Set;
-import org.apache.commons.lang3.StringUtils;
-
/**
* This processor is used to Perform Sampling on User specified sampling rate
*
@@ -41,14 +46,24 @@
@BuiltInProcessor("FixedRateSamplingTelemetryProcessor")
public final class FixedRateSamplingTelemetryProcessor implements TelemetryProcessor {
- private final String dependencyTelemetryName = "Dependency";
+ public static final double DEFAULT_SAMPLING_PERCENTAGE = 100.0;
+ private static Map allowedTypes = new HashMap<>();
+
+ private static final String dependencyTelemetryName = "Dependency";
private static final String eventTelemetryName = "Event";
private static final String exceptionTelemetryName = "Exception";
private static final String pageViewTelemetryName = "PageView";
private static final String requestTelemetryName = "Request";
private static final String traceTelemetryName = "Trace";
- private static Map allowedTypes;
+ static {
+ allowedTypes.put(dependencyTelemetryName, RemoteDependencyTelemetry.class);
+ allowedTypes.put(eventTelemetryName, EventTelemetry.class);
+ allowedTypes.put(exceptionTelemetryName, ExceptionTelemetry.class);
+ allowedTypes.put(pageViewTelemetryName, PageViewTelemetry.class);
+ allowedTypes.put(requestTelemetryName, RequestTelemetry.class);
+ allowedTypes.put(traceTelemetryName, TraceTelemetry.class);
+ }
private Set excludedTypes;
@@ -65,21 +80,10 @@ public final class FixedRateSamplingTelemetryProcessor implements TelemetryProce
* to default settings
*/
public FixedRateSamplingTelemetryProcessor() {
- this.samplingPercentage = 100.00;
- this.includedTypes = new HashSet();
- this.excludedTypes = new HashSet();
- try {
- this.allowedTypes = new HashMap() {{
- put(dependencyTelemetryName, com.microsoft.applicationinsights.telemetry.RemoteDependencyTelemetry.class);
- put(eventTelemetryName, com.microsoft.applicationinsights.telemetry.EventTelemetry.class);
- put(exceptionTelemetryName, com.microsoft.applicationinsights.telemetry.ExceptionTelemetry.class);
- put(pageViewTelemetryName, com.microsoft.applicationinsights.telemetry.PageViewTelemetry.class);
- put(requestTelemetryName, com.microsoft.applicationinsights.telemetry.RequestTelemetry.class);
- put(traceTelemetryName, com.microsoft.applicationinsights.telemetry.TraceTelemetry.class);
- }};
- } catch (Exception e) {
- InternalLogger.INSTANCE.trace("Unable to locate telemetry classes. stack trace is %s", ExceptionUtils.getStackTrace(e));
- }
+
+ this.samplingPercentage = DEFAULT_SAMPLING_PERCENTAGE;
+ this.includedTypes = new HashSet<>();
+ this.excludedTypes = new HashSet<>();
}
/**
@@ -108,10 +112,10 @@ private void setIncludedOrExcludedTypes(String value, Set typeSet) {
if (!StringUtils.isEmpty(value) && allowedTypes.containsKey(value)) {
typeSet.add(allowedTypes.get(value));
} else {
- InternalLogger.INSTANCE.error("Item is either not allowed to sample or is empty");
+ InternalLogger.INSTANCE.error("Item %s is either not allowed to sample or is empty", value);
}
} else {
- InternalLogger.INSTANCE.error("Empty types cannot be considered");
+ InternalLogger.INSTANCE.error("Telemetry type %s is empty", value);
}
}
diff --git a/core/src/main/java/com/microsoft/applicationinsights/internal/heartbeat/MiscUtils.java b/core/src/main/java/com/microsoft/applicationinsights/internal/heartbeat/MiscUtils.java
index de913e63ca1..e7eaf6e5317 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/internal/heartbeat/MiscUtils.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/internal/heartbeat/MiscUtils.java
@@ -9,7 +9,7 @@
*
* @author Dhaval Doshi
*/
-class MiscUtils {
+public class MiscUtils {
/**
* Returns a list which contains result of List - Set
@@ -17,7 +17,7 @@ class MiscUtils {
* @param set
* @return
*/
- static Set except(List list2, Set set) {
+ public static Set except(List list2, Set set) {
try {
if (set == null) {
throw new IllegalArgumentException("Input is null");
diff --git a/core/src/main/java/com/microsoft/applicationinsights/internal/perfcounter/PerformanceCounterContainer.java b/core/src/main/java/com/microsoft/applicationinsights/internal/perfcounter/PerformanceCounterContainer.java
index de11b112e6d..6fa1f9e5ce8 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/internal/perfcounter/PerformanceCounterContainer.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/internal/perfcounter/PerformanceCounterContainer.java
@@ -63,7 +63,7 @@ public enum PerformanceCounterContainer implements Stoppable {
private final static long START_DEFAULT_MIN_DELAY_IN_MILLIS = 20000;
// By default the container will collect performance data every 1 minute.
- private final static long DEFAULT_COLLECTION_FREQUENCY_IN_SEC = 60;
+ public final static long DEFAULT_COLLECTION_FREQUENCY_IN_SEC = 60;
private final static long MIN_COLLECTION_FREQUENCY_IN_SEC = 1;
private final ConcurrentMap performanceCounters = new ConcurrentHashMap();
diff --git a/core/src/main/java/com/microsoft/applicationinsights/internal/util/PropertyHelper.java b/core/src/main/java/com/microsoft/applicationinsights/internal/util/PropertyHelper.java
index 3d0dbc09031..62a49065178 100644
--- a/core/src/main/java/com/microsoft/applicationinsights/internal/util/PropertyHelper.java
+++ b/core/src/main/java/com/microsoft/applicationinsights/internal/util/PropertyHelper.java
@@ -33,6 +33,7 @@
*/
public final class PropertyHelper {
public final static String SDK_VERSION_FILE_NAME = "sdk-version.properties";
+ final static String STARTER_VERSION_FILE_NAME = "starter-version.properties";
/**
* Reads the properties from a properties file.
@@ -74,6 +75,21 @@ public static Properties getSdkVersionProperties() {
return null;
}
+ /**
+ * A method that loads the properties file that contains the AI SpringBootStarter version number
+ * @return The properties or null if not found.
+ */
+ public static Properties getStarterVersionProperties() {
+ try {
+ return getProperties(STARTER_VERSION_FILE_NAME);
+ }
+ catch (IOException e) {
+ InternalLogger.INSTANCE.trace("Could not find starter version file: %s,"
+ + "stack trace is: ", SDK_VERSION_FILE_NAME, ExceptionUtils.getStackTrace(e));
+ }
+ return null;
+ }
+
private PropertyHelper() {
}
}
diff --git a/core/src/test/java/com/microsoft/applicationinsights/internal/channel/inprocess/InProcessTelemetryChannelTest.java b/core/src/test/java/com/microsoft/applicationinsights/internal/channel/inprocess/InProcessTelemetryChannelTest.java
index eed12e3e5e2..9d4e489bfd4 100644
--- a/core/src/test/java/com/microsoft/applicationinsights/internal/channel/inprocess/InProcessTelemetryChannelTest.java
+++ b/core/src/test/java/com/microsoft/applicationinsights/internal/channel/inprocess/InProcessTelemetryChannelTest.java
@@ -22,14 +22,15 @@
package com.microsoft.applicationinsights.internal.channel.inprocess;
import com.microsoft.applicationinsights.channel.concrete.inprocess.InProcessTelemetryChannel;
+import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
public class InProcessTelemetryChannelTest {
- private final static String NON_VALID_URL = "http:sd{@~fsd.s.d.f;fffff";
- private final static String INSTANT_RETRY_NAME = "MaxInstantRetry";
+ private final static String NON_VALID_URL = "http:sd{@~fsd.s.d.f;fffff";
+ private final static String INSTANT_RETRY_NAME = "MaxInstantRetry";
private final static int DEFAULT_MAX_INSTANT_RETRY = 3;
@Test(expected = IllegalArgumentException.class)
@@ -44,18 +45,24 @@ public void testStringIntegerMaxInstanceRetry() {
map.put(INSTANT_RETRY_NAME, "AABB");
new InProcessTelemetryChannel(map);
}
-
+
@Test()
public void testValidIntegerMaxInstanceRetry() {
HashMap map = new HashMap();
map.put(INSTANT_RETRY_NAME, "4");
new InProcessTelemetryChannel(map);
}
-
+
@Test()
public void testInvalidIntegerMaxInstanceRetry() {
HashMap map = new HashMap();
map.put(INSTANT_RETRY_NAME, "-1");
new InProcessTelemetryChannel(map);
}
+
+ @Test
+ public void testInProcessTelemetryChannelWithDefaultSpringBootParameters() {
+ new InProcessTelemetryChannel("https://dc.services.visualstudio.com/v2/track", "10",
+ false, 500, 5, true, DEFAULT_MAX_INSTANT_RETRY);
+ }
}
\ No newline at end of file
diff --git a/core/src/test/java/com/microsoft/applicationinsights/internal/config/TelemetryConfigurationFactoryTest.java b/core/src/test/java/com/microsoft/applicationinsights/internal/config/TelemetryConfigurationFactoryTest.java
index 9781f8516f1..05a4af20ef1 100644
--- a/core/src/test/java/com/microsoft/applicationinsights/internal/config/TelemetryConfigurationFactoryTest.java
+++ b/core/src/test/java/com/microsoft/applicationinsights/internal/config/TelemetryConfigurationFactoryTest.java
@@ -23,6 +23,7 @@
import java.io.InputStream;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.*;
import com.google.common.base.Predicates;
@@ -398,6 +399,17 @@ public void testDefaultChannelWithBadData() {
assertEquals(mockConfiguration.getChannel().isDeveloperMode(), false);
}
+ @Test
+ public void testEmptyConfiguration() {
+ TelemetryConfiguration emptyConfig = TelemetryConfiguration.getActiveWithoutInitializingConfig();
+ Assert.assertEquals(null, emptyConfig.getInstrumentationKey());
+ Assert.assertEquals(null, emptyConfig.getChannel());
+ Assert.assertEquals(0, emptyConfig.getTelemetryModules().size());
+ Assert.assertEquals(false, emptyConfig.isTrackingDisabled());
+ Assert.assertEquals(0, emptyConfig.getContextInitializers().size());
+ Assert.assertEquals(0, emptyConfig.getTelemetryProcessors().size());
+ }
+
private MockTelemetryModule generateTelemetryModules(boolean addParameter) {
AppInsightsConfigurationBuilder mockParser = createMockParser(true, true, false);
ApplicationInsightsXmlConfiguration appConf = mockParser.build(null);
@@ -567,4 +579,11 @@ private void ikeyTest(String configurationIkey, String expectedIkey) {
assertEquals(mockConfiguration.getInstrumentationKey(), expectedIkey);
assertTrue(mockConfiguration.getChannel() instanceof InProcessTelemetryChannel);
}
+
+ @After
+ public void tearDown() throws Exception {
+ Method method = TelemetryConfiguration.class.getDeclaredMethod("setActiveAsNull");
+ method.setAccessible(true);
+ method.invoke(null);
+ }
}
diff --git a/settings.gradle b/settings.gradle
index 65a1e3e9969..3645ec1c991 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -26,6 +26,7 @@ include 'logging:log4j1_2'
include 'logging:log4j2'
include 'logging:logback'
include 'web'
+include 'azure-application-insights-spring-boot-starter'
include 'distributions'
include 'samples'
include 'test:performance'
@@ -59,3 +60,6 @@ if (System.env.'COLLECTD_HOME') {
include 'collectd'
}
+include ':test:smoke:testApps:SpringBootTest'
+
+
diff --git a/test/smoke/build.gradle b/test/smoke/build.gradle
index d565b81acd4..7c4f4a0cec6 100644
--- a/test/smoke/build.gradle
+++ b/test/smoke/build.gradle
@@ -22,6 +22,7 @@ subprojects {
aiLog4j1_2Jar = project(':logging:log4j1_2')
aiLo4j2Jar = project(':logging:log4j2')
aiLogbackJar = project(':logging:logback')
+ springBootStarterJar = project(':azure-application-insights-spring-boot-starter')
}
}
diff --git a/test/smoke/framework/testCore/src/main/java/com/microsoft/applicationinsights/smoketest/AiSmokeTest.java b/test/smoke/framework/testCore/src/main/java/com/microsoft/applicationinsights/smoketest/AiSmokeTest.java
index 1e43b88f172..e75ae5fe2c3 100644
--- a/test/smoke/framework/testCore/src/main/java/com/microsoft/applicationinsights/smoketest/AiSmokeTest.java
+++ b/test/smoke/framework/testCore/src/main/java/com/microsoft/applicationinsights/smoketest/AiSmokeTest.java
@@ -310,6 +310,7 @@ protected void callTargetUriAndWaitForTelemetry() throws Exception {
}
System.out.println("Calling "+targetUri+" ...");
String url = getBaseUrl()+targetUri;
+ System.out.println("calling " + url);
final String content;
switch(httpMethod) {
case "GET":
diff --git a/test/smoke/testApps/SpringBootTest/build.gradle b/test/smoke/testApps/SpringBootTest/build.gradle
new file mode 100644
index 00000000000..6ad8a668e7f
--- /dev/null
+++ b/test/smoke/testApps/SpringBootTest/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'war'
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE'
+ }
+}
+
+apply plugin: 'org.springframework.boot'
+
+compileJava.sourceCompatibility = 1.7
+compileJava.targetCompatibility = 1.7
+compileSmokeTestJava.sourceCompatibility = 1.8
+compileSmokeTestJava.targetCompatibility = 1.8
+
+dependencies {
+ compile springBootStarterJar
+ compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.9.RELEASE'
+ compile group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: '1.5.9.RELEASE'
+ testCompile group: 'junit', name: 'junit', version: '4.12'
+}
+
+configurations {
+ smokeTestCompile.exclude group:'org.springframework.boot'
+ smokeTestRuntime.exclude group:'org.springframework.boot'
+}
+
+ext.testAppArtifactDir = war.destinationDir
+ext.testAppArtifactFilename = war.archiveName
\ No newline at end of file
diff --git a/test/smoke/testApps/SpringBootTest/src/main/java/com/SpringBootApp.java b/test/smoke/testApps/SpringBootTest/src/main/java/com/SpringBootApp.java
new file mode 100644
index 00000000000..2e1dd842b30
--- /dev/null
+++ b/test/smoke/testApps/SpringBootTest/src/main/java/com/SpringBootApp.java
@@ -0,0 +1,18 @@
+package com;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+
+@SpringBootApplication
+public class SpringBootApp extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder applicationBuilder) {
+ return applicationBuilder.sources(SpringBootApp.class);
+ }
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootApp.class, args);
+ }
+}
diff --git a/test/smoke/testApps/SpringBootTest/src/main/java/com/springbootstartertest/controller/TestController.java b/test/smoke/testApps/SpringBootTest/src/main/java/com/springbootstartertest/controller/TestController.java
new file mode 100644
index 00000000000..db0efc0555e
--- /dev/null
+++ b/test/smoke/testApps/SpringBootTest/src/main/java/com/springbootstartertest/controller/TestController.java
@@ -0,0 +1,39 @@
+package com.springbootstartertest.controller;
+
+import com.microsoft.applicationinsights.TelemetryClient;
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class TestController {
+
+ @Autowired
+ TelemetryClient client;
+
+ @GetMapping("/")
+ public String rootPage() {
+ return "OK";
+ }
+
+ @GetMapping("/basic/trackEvent")
+ public String trackEventSpringBoot() {
+ Map properties = new HashMap() {
+ {
+ put("key", "value");
+ }
+ };
+ Map metrics = new HashMap() {
+ {
+ put("key", 1d);
+ }
+ };
+
+ //Event
+ client.trackEvent("EventDataTest");
+ client.trackEvent("EventDataPropertyTest", properties, metrics);
+ return "hello";
+ }
+}
diff --git a/test/smoke/testApps/SpringBootTest/src/main/resources/application.properties b/test/smoke/testApps/SpringBootTest/src/main/resources/application.properties
new file mode 100644
index 00000000000..741e0983b10
--- /dev/null
+++ b/test/smoke/testApps/SpringBootTest/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+spring.application.name=SpringBootTest
+azure.application-insights.channel.in-process.endpoint-address=http://fakeingestion:60606/v2/track
+azure.application-insights.instrumentation-key=00000000-0000-0000-0000-cba987654321
+azure.application-insights.logger.level=TRACE
+azure.application-insights.default-modules.ProcessPerformanceCountersModule.enabled=false
diff --git a/test/smoke/testApps/SpringBootTest/src/smokeTest/java/com/springbootstartertest/smoketest/SpringbootSmokeTest.java b/test/smoke/testApps/SpringBootTest/src/smokeTest/java/com/springbootstartertest/smoketest/SpringbootSmokeTest.java
new file mode 100644
index 00000000000..872cedfaf28
--- /dev/null
+++ b/test/smoke/testApps/SpringBootTest/src/smokeTest/java/com/springbootstartertest/smoketest/SpringbootSmokeTest.java
@@ -0,0 +1,37 @@
+package com.springbootstartertest.smoketest;
+
+import static org.junit.Assert.assertEquals;
+
+import com.microsoft.applicationinsights.internal.schemav2.EventData;
+import com.microsoft.applicationinsights.smoketest.AiSmokeTest;
+import com.microsoft.applicationinsights.smoketest.TargetUri;
+import org.junit.Test;
+
+public class SpringbootSmokeTest extends AiSmokeTest{
+
+ @Test
+ @TargetUri("/basic/trackEvent")
+ public void trackEvent() throws Exception {
+ assertEquals(1, mockedIngestion.getCountForType("RequestData"));
+ assertEquals(2, mockedIngestion.getCountForType("EventData"));
+ int totalItems = mockedIngestion.getItemCount();
+ int expectedItems = 3;
+ assertEquals(String.format("There were %d extra telemetry items received.", expectedItems - totalItems),
+ expectedItems, totalItems);
+
+ // TODO get event data envelope and verify value
+ EventData d = getTelemetryDataForType(0, "EventData");
+ final String name = "EventDataTest";
+ assertEquals(name, d.getName());
+
+ EventData d2 = getTelemetryDataForType(1, "EventData");
+
+ final String expectedName = "EventDataPropertyTest";
+ final String expectedProperties = "value";
+ final Double expectedMetric = 1d;
+
+ assertEquals(expectedName, d2.getName());
+ assertEquals(expectedProperties, d2.getProperties().get("key"));
+ assertEquals(expectedMetric, d2.getMeasurements().get("key"));
+ }
+}
diff --git a/test/smoke/testApps/SpringBootTest/src/smokeTest/resources/appServers.txt b/test/smoke/testApps/SpringBootTest/src/smokeTest/resources/appServers.txt
new file mode 100644
index 00000000000..ec0fa258a85
--- /dev/null
+++ b/test/smoke/testApps/SpringBootTest/src/smokeTest/resources/appServers.txt
@@ -0,0 +1 @@
+tomcat8
\ No newline at end of file