Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Added application insights starter for spring boot #518

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions azure-application-insights-spring-boot-starter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@

**Application Insights Spring Boot Starter**

This Starter provides you the minimal and required configuration to use Application Insights in your Spring Boot application.

**Requirements**
Spring Boot 1.5+ or 2.0+

**Quick Start**

*1. Add dependency*
Gradle:
```groovy
compile "com.microsoft.azure:azure-application-insights-spring-boot-starter:${version}"
```

Maven:
```xml
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-application-insights-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```

*2. Provide Instrumentation Key*

Add property
```
azure.application-insights.instrumentation-key=<key from the Azure Portal>
```
into your `application.properties`

*3. Run your application*

Start your spring boot application as usual and in few minutes you'll start getting events.

**Additional Configuration**

#### Sending custom telemetry
```java
@RestController
public class TelemetryController {

@Autowired
private TelemetryClient telemetryClient;

@RequestMapping("/telemetry")
public void telemetry() {
telemetryClient.trackEvent("my event");
}
}
```


#### Sending logs to the application insight

Follow the instructions from [Spring Boot logging documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html) to configure custom logback or log4j2 appender.

`logback-spring.xml`:
```xml
<appender name="aiAppender"
class="com.microsoft.applicationinsights.logback.ApplicationInsightsAppender">
</appender>
<root level="trace">
<appender-ref ref="aiAppender" />
</root>
```

`log4j2.xml`:
```xml
<Configuration packages="com.microsoft.applicationinsights.log4j.v2">
<Appenders>
<ApplicationInsightsAppender name="aiAppender" />
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="aiAppender"/>
</Root>
</Loggers>
</Configuration>
```

#### Register own telemetry module, processor or initializer by defining it as a bean in the configuration
```java
@SpringBootApplication
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

@Bean
public TelemetryModule myTelemetryModule() {
return new MyTelemetryModule();
}

@Bean
public TelemetryInitializer myTelemetryInitializer() {
return new MyTelemetryInitializer();
}

@Bean
public TelemetryProcessor myTelemetryProcessor() {
return new MyTelemetryProcessor();
}

@Bean
public ContextInitializer myContextInitializer() {
return new MyContextInitializer();
}
}
```


#### Configure more parameters using `application.properties`
```properties
# Instrumentation key from the Azure Portal. Required.
azure.application-insights.instrumentation-key=00000000-0000-0000-0000-000000000000

# Enable/Disable tracking. Default value: true.
azure.application-insights.enabled=true
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question as below for defaults.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By default it's true and should not be specified to run application. Only single required property is azure.application-insights.instrumentation-key

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense.


# Enable/Disable web modules. Default value: true.
azure.application-insights.web.enabled=true

# Logging type [console, file]. Default value: console.
azure.application-insights.logger.type=console
# Logging level [all, trace, info, warn, error, off]. Default value: error.
azure.application-insights.logger.level=error

# Enable/Disable default telemetry modules. Default value: true.
azure.application-insights.default-modules.ProcessPerformanceCountersModule.enabled=true
azure.application-insights.default-modules.WebRequestTrackingTelemetryModule.enabled=true
azure.application-insights.default-modules.WebSessionTrackingTelemetryModule.enabled=true
azure.application-insights.default-modules.WebUserTrackingTelemetryModule.enabled=true
azure.application-insights.default-modules.WebPerformanceCounterModule.enabled=true
azure.application-insights.default-modules.WebOperationIdTelemetryInitializer.enabled=true
azure.application-insights.default-modules.WebOperationNameTelemetryInitializer.enabled=true
azure.application-insights.default-modules.WebSessionTelemetryInitializer.enabled=true
azure.application-insights.default-modules.WebUserTelemetryInitializer.enabled=true
azure.application-insights.default-modules.WebUserAgentTelemetryInitializer.enabled=true
```
51 changes: 51 additions & 0 deletions azure-application-insights-spring-boot-starter/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.
*/

apply from: "$buildScriptsDir/common-java.gradle"
apply from: "$buildScriptsDir/publishing.gradle"

archivesBaseName = 'azure-application-insights-spring-boot-starter'

dependencies {
Copy link
Contributor

Choose a reason for hiding this comment

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

bunch of new dependencies. We need to be bit careful what is absolutely needed and also register them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we have everything needed here to use with spring boot, only new dependency is assertj and junit of newer version and both testCompile.

Copy link
Contributor

Choose a reason for hiding this comment

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

@gavlyukovskiy Isn't junit 5.x still in beta?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, it was released in September and actually yesterday they did second GA release - 5.1.0 :)

compile (project(':core'))
compile (project(':web'))
provided('org.springframework.boot:spring-boot:1.5.9.RELEASE')
provided('org.springframework.boot:spring-boot-autoconfigure:1.5.9.RELEASE')
provided('org.springframework.boot:spring-boot-starter-web:1.5.9.RELEASE')
provided('org.springframework.boot:spring-boot-configuration-processor:1.5.9.RELEASE')

testCompile('org.junit.jupiter:junit-jupiter-api:5.0.2')
testCompile('org.junit.jupiter:junit-jupiter-params:5.0.2')
testCompile('org.junit.jupiter:junit-jupiter-engine:5.0.2')
testCompile('org.springframework.boot:spring-boot-starter-test:1.5.9.RELEASE')
testCompile('org.assertj:assertj-core:3.9.0')
}

// region Publishing properties

projectPomName = project.msftAppInsightsJavaSdk + " Spring Boot starter"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this POM project name consistent with other AI pom generation scripts? For example look at the build.gradle in core module. It is slightly different. How does this difference affect here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I took web module to copy this I intentionally did not include shadowJar because I have no need in it.

ProjectPomName actually is equal to Microsoft Application Insights Java SDK Spring Boot starter, I don't know why other modules use msftAppInsights and then add ' Java SDK', for example project.msftAppInsights + " Java SDK Core" == project.msftAppInsightsJavaSdk + ' Core'

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, shadow jar is not needed in this case. Shadowing is usually done only for dependencies that we need to encapsulate like Apache HTTP Client. In this case the SpringBoot Starter jar will be shipped as a jar and it will pull all the transitive dependencies once loaded into the SpringBoot Project, so we do not need that.

projectPomDescription = "This is the Spring Boot starter of " + project.msftAppInsightsJavaSdk

whenPomConfigured = { p ->
def agentArtifactId = project(":agent").jar.baseName
p.dependencies = p.dependencies.findAll { dep -> dep.artifactId != agentArtifactId }
writePomToArtifactsDirectory(p, project.name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.boot.conditional.ConditionalOnOperatingSystem;
import com.microsoft.applicationinsights.boot.conditional.OperatingSystem;
import com.microsoft.applicationinsights.boot.initializer.SpringBootContextInitializer;
import com.microsoft.applicationinsights.extensibility.initializer.DeviceInfoContextInitializer;
import com.microsoft.applicationinsights.extensibility.initializer.SdkVersionContextInitializer;
import com.microsoft.applicationinsights.internal.perfcounter.ProcessPerformanceCountersModule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
* {@link Configuration} for non-web applications.
*
* @author Arthur Gavlyukovskiy
*/
@Configuration
public class ApplicationInsightsModuleConfiguration {

@Bean
public SpringBootContextInitializer springBootContextInitializer(Environment environment) {
return new SpringBootContextInitializer(environment);
}

@Bean
public SdkVersionContextInitializer sdkVersionContextInitializer() {
return new SdkVersionContextInitializer();
}

@Bean
public DeviceInfoContextInitializer deviceInfoContextInitializer() {
return new DeviceInfoContextInitializer();
}

@Bean
@ConditionalOnOperatingSystem(OperatingSystem.WINDOWS)
@ConditionalOnProperty(value = "azure.application-insights.default-modules.ProcessPerformanceCountersModule.enabled", havingValue = "true", matchIfMissing = true)
public ProcessPerformanceCountersModule processPerformanceCountersModule() {
try {
return new ProcessPerformanceCountersModule();
}
catch (Exception e) {
throw new IllegalStateException("Could not initialize Windows performance counters module", e);
Copy link
Contributor

Choose a reason for hiding this comment

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

The throw from here means it would crash the user application? In that event it is much advised to rather log the message using logger rather than throwing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will change this to logging error.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I remember why I did it, actually you can't just log error here because bean instance must be returned. I'm not quite sure how to fix this properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although I don't see any reason why it would throw an exception. ProcessBuiltInPerformanceCountersFactory is instance of WindowsPerformanceCountersFactory and if something goes wrong inside this factory it returns empty list.

Choose a reason for hiding this comment

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

Seems like very unlikely situation. Can't you return null in such case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it's possible, in best case we can either create conditional where we try to create instance and return true only if it was successfully created, or have message with clear action to do, something like 'We are failed to create performance metric, please set azure.application-insights.default-modules.ProcessPerformanceCountersModule.enabled=false to avoid this error message'. But in general I don't see any chance for this exception to be thrown in real code. @dhaval24 what do you think?

Choose a reason for hiding this comment

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

@gavlyukovskiy from technical point of view it's possible, such bean declaration is 100% valid and it works:
@Bean public CustomBean customBean(){ return null; }

Copy link
Contributor Author

@gavlyukovskiy gavlyukovskiy Feb 22, 2018

Choose a reason for hiding this comment

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

You're right, it's valid. But then I should check that module is not null when initializing them in TelemetryConfiguration, I can do this as well, but it just seem useless to me to add a lot of checks that verify situation that can not happen. That's why I throw IllegalStateException here. If there are will be issue with that it indicates bug on our side and you can't save user from all bugs you may ever have. Although user has simple switch to disable this module so it won't be a blocker anyways.

Copy link
Contributor

Choose a reason for hiding this comment

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

@gavlyukovskiy I am fine with failing and asking to set the message please set azure.application-insights.default-modules.ProcessPerformanceCountersModule.enabled=false to avoid this error message'. Please add this conditional.

}
}
}
Loading