Skip to content

Conversation

@gavlyukovskiy
Copy link
Contributor

Fix #359, revisit #411.

Adds a way to configure TelemetryConfiguration from outside application insights library.
It is a must for spring boot integration because you can retrieve configuration only from spring context itself.
Whereas the same can be achieved by creating yet another @Configuration class, just like InterceptorRegistry, and asking user to import that one, it also can be accomplished by creating a spring boot starter with our full control of context initialization order.
I've prepared such starter that links with my forked version of sdk and once is added, requires only single property azure.application-insights.instrumentation-key to be set to integrate spring boot application with Application Insights.
As well allows you to use spring context to manage TelemetryConfiguration components - you can define any telemetry module, channel or sampler as @Bean and it will be used inside TelemetryConfiguration. Also it adds nice spring boot feature such like configuration metadata -
when IDE shows you the list of available configuration properties.

Conceptually all changes are backward compatible and only concern is that all modules that are using TelemetryConfiguration#getActive() must be initialized after TelemetryConfiguration is created by spring context, which is not possible for logger appenders, that's why I added lazy initialization inside logback appender (log4j and log4j2 can be modified in the same way) which leads to loss of initial log messages. If you think it is critical I can implement some kind of a buffer that will hold telemetry events until configuration is initialized.

You can look at my starter project to find more details. Also I can upload my sample project that uses forked version of sdk and the starter.

@dhaval24
Copy link
Contributor

dhaval24 commented Nov 7, 2017

@gavlyukovskiy Thank you very much for your contribution towards enabling spring boot support with Application Insights. Amazing work! I have added reviewers to your pull request and we will review it. Lets have a conversation around this and we shall take it from there. On a brief overview I feel this is a good concept.

Copy link
Contributor

@SergeyKanzhelev SergeyKanzhelev left a comment

Choose a reason for hiding this comment

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

I left a comment. Maybe I do not understand the whole context - will be happy to learn more

* Telemetry configuration must not be set twice explicitly or through calling {@link TelemetryConfiguration#getActive()}.
*
* @param telemetryConfiguration preconfigured instance of Telemetry Configuration
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

We found it messy to allow to replace the configuration object. As you mentioned in comments one of the modules may get a hold of an object and will not pick up an update. Would it be better to update properties of an object instead?

With dependency injection we would simply initialize singleton of dependency injection with the value of telemetryconfiguration.active

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's why I added throw to be sure that configuration is initialized only once and no module is initialized with wrong TelemetryConfiguration. Probably configuration could be changed once context is loaded but I'm afraid that some module can get properties from the TelemetryConfiguration when those properties are not set yet - like WebRequestTrackingFilter#registerWebApp that is executed only once.

*/
public static void setActive(TelemetryConfiguration telemetryConfiguration) {
synchronized (s_lock) {
if (initialized) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Public APIs do not throw in Application Insights SDK

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Sergey's point here. Throwing an exception in public API will crash the customers application and prevent it from starting. It is not a desired behavior.

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 would agree with you about runtime exceptions, but this one is startup exception, where, from my point of view, is better to get exception when something goes wrong rather than found that telemetry is not working after application is started. But if this is a strong sdk convention, I can just log error into logs.

Copy link
Contributor

@dhaval24 dhaval24 left a comment

Choose a reason for hiding this comment

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

I have added some common observations and comments. We can discuss this in detail.

*/
public static void setActive(TelemetryConfiguration telemetryConfiguration) {
synchronized (s_lock) {
if (initialized) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Sergey's point here. Throwing an exception in public API will crash the customers application and prevent it from starting. It is not a desired behavior.

public static void setActive(TelemetryConfiguration telemetryConfiguration) {
synchronized (s_lock) {
if (initialized) {
throw new IllegalStateException("Telemetry configuration is already initialized. " +
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we some how prevent this behavior programaticallly? If some one tries to call setActive() twice the second call is ignored?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Answered in previous thread, this is probably an option, but I wouldn't implement it like that, it is too dangerous and hard to debug if something goes wrong, it should not be initialized more than once.

name = telemetryConfiguration.getApplicationName();
}
else if (CommonUtils.isNullOrEmpty(contextPath)) {
URL[] jarPaths = ((URLClassLoader) (this.getClass().getClassLoader())).getURLs();
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an issue with JDK 9. The default class loader of JDK 9 is not an instance of URLClassLoader but is instead called "App-Classloader". This call to getURLs() would fail here. Do we have a work around for this ? Have you tried this on a Spring Boot Project with JDK 9?

Copy link
Contributor Author

@gavlyukovskiy gavlyukovskiy Nov 7, 2017

Choose a reason for hiding this comment

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

I haven't tried it on JDK 9, but probably ClassLoader#getResources/ClassLoader#getSystemResources will do the job.

if (CommonUtils.isNullOrEmpty(contextPath)) {
TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.getActive();
if (telemetryConfiguration.getApplicationName() != null) {
name = telemetryConfiguration.getApplicationName();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need a check here if someone has tried setting application name as null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I will add check into TelemetryConfiguration#setApplicationName that name is not null or empty.

@gavlyukovskiy
Copy link
Contributor Author

I created repo with sample project here it shows how it is easy to start with starter in spring boot application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants