Skip to content

Commit 07cfd6f

Browse files
authored
Help the user to manage the OpenTelemetry version in Spring Monitor (Azure#36610)
1 parent 1694cbd commit 07cfd6f

File tree

9 files changed

+282
-35
lines changed

9 files changed

+282
-35
lines changed

eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ the main ServiceBusClientBuilder. -->
429429
<!-- GoodLoggingCheck: Spring as a framework, will not directly use ClientLogger-->
430430
<suppress checks="com.azure.tools.checkstyle.checks.(GoodLoggingCheck|ThrowFromClientLoggerCheck|UseCaughtExceptionCauseCheck)" files=".*[/\\]com[/\\]azure[/\\]spring[/\\].*"/>
431431

432+
<!-- Can be removed after https://github.com/Azure/azure-sdk-for-java/issues/36609 -->
433+
<suppress checks="com.azure.tools.checkstyle.checks.(GoodLoggingCheck|ThrowFromClientLoggerCheck|UseCaughtExceptionCauseCheck)" files="com.azure.monitor.applicationinsights.spring.OpenTelemetryVersionCheckRunner.java"/>
434+
432435
<!-- ExternalDependencyExposedCheck: Spring will directly use classes from spring dependencies in methods-->
433436
<suppress checks="com.azure.tools.checkstyle.checks.ExternalDependencyExposedCheck" files=".*[/\\]com[/\\]azure[/\\]spring[/\\].*"/>
434437

sdk/spring/spring-cloud-azure-starter-monitor-test/src/test/java/com/azure/SpringMonitorTest.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
import static java.util.concurrent.TimeUnit.SECONDS;
66
import static org.assertj.core.api.Assertions.*;
7-
import static org.assertj.core.api.Assertions.assertThat;
87

98
import com.azure.core.http.*;
109
import com.azure.core.http.policy.HttpPipelinePolicy;
10+
import com.azure.monitor.applicationinsights.spring.OpenTelemetryVersionCheckRunner;
1111
import com.azure.monitor.opentelemetry.exporter.implementation.models.*;
1212
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
1313
import io.opentelemetry.sdk.metrics.export.MetricExporter;
14+
import io.opentelemetry.sdk.resources.Resource;
1415
import io.opentelemetry.sdk.trace.export.SpanExporter;
15-
16-
import java.io.InputStream;
16+
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
1717
import java.net.MalformedURLException;
1818
import java.net.URL;
1919
import java.util.List;
@@ -43,13 +43,15 @@ public class SpringMonitorTest {
4343
@Autowired private TestRestTemplate restTemplate;
4444

4545
// See io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration
46-
@Autowired ObjectProvider<List<SpanExporter>> otelSpanExportersProvider;
46+
@Autowired private ObjectProvider<List<SpanExporter>> otelSpanExportersProvider;
4747

4848
// See io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration
49-
@Autowired ObjectProvider<List<LogRecordExporter>> otelLoggerExportersProvider;
49+
@Autowired private ObjectProvider<List<LogRecordExporter>> otelLoggerExportersProvider;
5050

5151
// See io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration
52-
@Autowired ObjectProvider<List<MetricExporter>> otelMetricExportersProvider;
52+
@Autowired private ObjectProvider<List<MetricExporter>> otelMetricExportersProvider;
53+
54+
@Autowired private Resource otelResource;
5355

5456
@Configuration(proxyBeanMethods = false)
5557
static class TestConfiguration {
@@ -166,4 +168,15 @@ public void shouldMonitor() throws InterruptedException, MalformedURLException {
166168
assertThat(requestData.getResponseCode()).isEqualTo("200");
167169
assertThat(requestData.getName()).isEqualTo("GET /controller-url");
168170
}
171+
172+
@Test
173+
public void verifyOpenTelemetryVersion() {
174+
String currentOTelVersion = otelResource.getAttribute(ResourceAttributes.TELEMETRY_SDK_VERSION);
175+
assertThat(OpenTelemetryVersionCheckRunner.STARTER_OTEL_VERSION)
176+
.as(
177+
"Dear developer, You may have updated the OpenTelemetry dependencies of spring-cloud-azure-starter-monitor without updating the OTel starter version declared in "
178+
+ OpenTelemetryVersionCheckRunner.class
179+
+ ".")
180+
.isEqualTo(currentOTelVersion);
181+
}
169182
}

sdk/spring/spring-cloud-azure-starter-monitor/README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,47 @@ For more information, please read [introduction to Application Insights][applica
2727
```
2828
[//]: # ({x-version-update-end})
2929

30-
This dependency is a Spring Boot starter that provides telemetry data for HTTP requests.
30+
You may have to align the OpenTelemetry versions of Spring Boot 3 and `spring-cloud-azure-starter-monitor`. If this is the case, you will notice a WARN message during the application start-up:
31+
```
32+
WARN c.a.m.a.s.OpenTelemetryVersionCheckRunner - The OpenTelemetry version is not compatible with the spring-cloud-azure-starter-monitor dependency. The OpenTelemetry version should be
33+
```
34+
To fix this with Maven, you can set the `opentelemetry.version` property:
35+
36+
```xml
37+
<properties>
38+
<opentelemetry.version>{otel-version-given-in-the-warn-log}</opentelemetry.version>
39+
</properties>
40+
```
41+
42+
Another way is to declare the `opentelemetry-bom` BOM:
43+
44+
```xml
45+
<dependencyManagement>
46+
<dependencies>
47+
<dependency>
48+
<groupId>io.opentelemetry</groupId>
49+
<artifactId>opentelemetry-bom</artifactId>
50+
<version>{otel-version-given-in-the-warn-log}</version>
51+
<type>pom</type>
52+
</dependency>
53+
</dependencies>
54+
</dependencyManagement>
55+
```
56+
57+
With Gradle, you can fix the issue in this way:
58+
59+
```
60+
plugins {
61+
id 'org.springframework.boot'
62+
id 'io.spring.dependency-management'
63+
}
64+
65+
dependencyManagement {
66+
imports {
67+
mavenBom 'io.opentelemetry:opentelemetry-bom:{otel-version-given-in-the-warn-log}'
68+
}
69+
}
70+
```
3171

3272
### Authentication
3373

sdk/spring/spring-cloud-azure-starter-monitor/pom.xml

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,47 @@
5757
<artifactId>opentelemetry-runtime-telemetry-java8</artifactId>
5858
<version>1.28.0-alpha</version> <!-- {x-version-update;io.opentelemetry.instrumentation:opentelemetry-runtime-telemetry-java8;external_dependency} -->
5959
</dependency>
60+
<dependency>
61+
<groupId>io.opentelemetry</groupId>
62+
<artifactId>opentelemetry-api-logs</artifactId>
63+
<version>1.26.0-alpha</version> <!-- {x-version-update;io.opentelemetry:opentelemetry-api-logs;external_dependency} -->
64+
<scope>compile</scope>
65+
</dependency>
6066
<!-- End of OTel dependencies -->
6167

6268
<dependency>
6369
<groupId>com.azure</groupId>
6470
<artifactId>azure-monitor-opentelemetry-exporter</artifactId>
6571
<version>1.0.0-beta.11</version> <!-- {x-version-update;com.azure:azure-monitor-opentelemetry-exporter;dependency} -->
6672
</dependency>
73+
74+
<!-- Test dependencies -->
6775
<dependency>
68-
<groupId>io.opentelemetry</groupId>
69-
<artifactId>opentelemetry-api-logs</artifactId>
70-
<version>1.26.0-alpha</version> <!-- {x-version-update;io.opentelemetry:opentelemetry-api-logs;external_dependency} -->
71-
<scope>compile</scope>
76+
<groupId>org.junit.jupiter</groupId>
77+
<artifactId>junit-jupiter-api</artifactId>
78+
<version>5.9.3</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-api;external_dependency} -->
79+
<scope>test</scope>
80+
</dependency>
81+
<dependency>
82+
<groupId>org.junit.jupiter</groupId>
83+
<artifactId>junit-jupiter-engine</artifactId>
84+
<version>5.9.3</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-engine;external_dependency} -->
85+
<scope>test</scope>
86+
</dependency>
87+
<dependency>
88+
<groupId>org.assertj</groupId>
89+
<artifactId>assertj-core</artifactId>
90+
<version>3.22.0</version> <!-- {x-version-update;org.assertj:assertj-core;external_dependency} -->
91+
<scope>test</scope>
92+
</dependency>
93+
<dependency>
94+
<groupId>org.springframework.boot</groupId>
95+
<artifactId>spring-boot-starter-test</artifactId>
96+
<version>2.7.14</version> <!-- {x-version-update;org.springframework.boot:spring-boot-starter-test;external_dependency} -->
97+
<scope>test</scope>
7298
</dependency>
7399

100+
<!-- End of test dependencies -->
74101

75102
</dependencies>
76103

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.monitor.applicationinsights.spring;
4+
5+
class OTelVersion {
6+
7+
private final String otelVersionAsString;
8+
private final int majorVersion;
9+
private final int minorVersion;
10+
private final int patchVersion;
11+
12+
OTelVersion(String otelVersionAsString) {
13+
this.otelVersionAsString = otelVersionAsString;
14+
String[] versionComponents = otelVersionAsString.split("\\.");
15+
this.majorVersion = Integer.parseInt(versionComponents[0]);
16+
this.minorVersion = Integer.parseInt(versionComponents[1]);
17+
this.patchVersion = Integer.parseInt(versionComponents[2]);
18+
}
19+
20+
boolean isLessThan(OTelVersion oTelVersion) {
21+
if (this.otelVersionAsString.equals(oTelVersion.otelVersionAsString)) {
22+
return false;
23+
}
24+
return !isGreaterThan(oTelVersion);
25+
}
26+
27+
boolean isGreaterThan(OTelVersion oTelVersion) {
28+
if (this.otelVersionAsString.equals(oTelVersion.otelVersionAsString)) {
29+
return false;
30+
}
31+
if (this.majorVersion > oTelVersion.majorVersion) {
32+
return true;
33+
}
34+
if (this.minorVersion > oTelVersion.minorVersion) {
35+
return true;
36+
}
37+
if (this.patchVersion > oTelVersion.patchVersion) {
38+
return true;
39+
}
40+
return false;
41+
}
42+
43+
boolean hasSameMajorVersionAs(OTelVersion oTelVersion) {
44+
return this.majorVersion == oTelVersion.majorVersion;
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.monitor.applicationinsights.spring;
4+
5+
import io.opentelemetry.sdk.resources.Resource;
6+
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
7+
import org.springframework.boot.CommandLineRunner;
8+
import org.springframework.stereotype.Component;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
/**
13+
* This component alerts the user to the fact that the OpenTelemetry version used is not compatible
14+
* with the starter. One use case is Spring Boot 3 using OpenTelemetry.
15+
*/
16+
@Component
17+
public class OpenTelemetryVersionCheckRunner implements CommandLineRunner {
18+
private static final Logger LOG = LoggerFactory.getLogger(OpenTelemetryVersionCheckRunner.class);
19+
20+
// If this version is not up-to-date, a test will fail.
21+
/**
22+
* OpenTelemetry version of the starter
23+
*/
24+
public static final String STARTER_OTEL_VERSION = "1.28.0";
25+
26+
private final Resource otelResource;
27+
28+
/**
29+
* Component to check the OpenTelemetry version.
30+
*
31+
* @param otelResource OpenTelemetry resource
32+
*/
33+
public OpenTelemetryVersionCheckRunner(Resource otelResource) {
34+
this.otelResource = otelResource;
35+
}
36+
37+
/**
38+
* To verify the OpenTelemetry version at the application start-up.
39+
*
40+
* @param args args
41+
*/
42+
@Override
43+
public void run(String... args) {
44+
try {
45+
String currentOTelVersionAsString = otelResource.getAttribute(ResourceAttributes.TELEMETRY_SDK_VERSION);
46+
OTelVersion currentOtelVersion = new OTelVersion(currentOTelVersionAsString);
47+
OTelVersion starterOTelVersion = new OTelVersion(STARTER_OTEL_VERSION);
48+
checkOpenTelemetryVersion(currentOtelVersion, starterOTelVersion);
49+
} catch (Exception e) {
50+
LOG.warn(
51+
"An unexpected issue has happened during the verification of the OpenTelemetry version.",
52+
e);
53+
}
54+
}
55+
56+
private static void checkOpenTelemetryVersion(
57+
OTelVersion currentOTelVersion, OTelVersion starterOTelVersion) {
58+
if (!currentOTelVersion.hasSameMajorVersionAs(starterOTelVersion) && currentOTelVersion.isLessThan(starterOTelVersion)) {
59+
LOG.warn(
60+
"The OpenTelemetry version is not compatible with the spring-cloud-azure-starter-monitor dependency. The OpenTelemetry version should be "
61+
+ STARTER_OTEL_VERSION
62+
+ ". "
63+
+ "Please look at the spring-cloud-azure-starter-monitor documentation to fix this.");
64+
} else if (currentOTelVersion.isGreaterThan(starterOTelVersion)) {
65+
LOG.info(
66+
"A new version of spring-cloud-azure-starter-monitor dependency may be available.");
67+
}
68+
}
69+
}

sdk/spring/spring-cloud-azure-starter-monitor/src/main/resources/META-INF/native-image/reflect-config.json

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,4 @@
11
[
2-
{
3-
"name": "io.opentelemetry.internal.shaded.jctools.queues.MpscArrayQueueConsumerIndexField",
4-
"fields": [
5-
{
6-
"name": "consumerIndex"
7-
}
8-
]
9-
},
10-
{
11-
"name": "io.opentelemetry.internal.shaded.jctools.queues.MpscArrayQueueProducerIndexField",
12-
"fields": [
13-
{
14-
"name": "producerIndex"
15-
}
16-
]
17-
},
18-
{
19-
"name": "io.opentelemetry.internal.shaded.jctools.queues.MpscArrayQueueProducerLimitField",
20-
"fields": [
21-
{
22-
"name": "producerLimit"
23-
}
24-
]
25-
},
262
{
273
"name": "io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender",
284
"allDeclaredMethods": true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.monitor.applicationinsights.spring;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
9+
class OTelVersionTest {
10+
11+
@Test
12+
void lessThanWithSameVersions() {
13+
assertThat(new OTelVersion("1.28.0").isLessThan(new OTelVersion("1.28.0"))).isFalse();
14+
}
15+
16+
@Test
17+
void lessThanWithSameMajorVersions() {
18+
assertThat(new OTelVersion("1.27.0").isLessThan(new OTelVersion("1.28.0"))).isTrue();
19+
}
20+
21+
@Test
22+
void lessThanWithSameMajorAndMinorVersions() {
23+
assertThat(new OTelVersion("1.28.0").isLessThan(new OTelVersion("1.28.1"))).isTrue();
24+
}
25+
26+
@Test
27+
void lessThanWithDifferentMajorVersions() {
28+
assertThat(new OTelVersion("1.27.0").isLessThan(new OTelVersion("2.28.0"))).isTrue();
29+
}
30+
31+
@Test
32+
void greaterThanWithSameVersions() {
33+
assertThat(new OTelVersion("1.28.0").isGreaterThan(new OTelVersion("1.28.0"))).isFalse();
34+
}
35+
36+
@Test
37+
void greaterThanWithSameMajorVersions() {
38+
assertThat(new OTelVersion("1.28.0").isGreaterThan(new OTelVersion("1.27.0"))).isTrue();
39+
}
40+
41+
@Test
42+
void greaterThanWithSameMajorAndMinorVersions() {
43+
assertThat(new OTelVersion("1.28.1").isGreaterThan(new OTelVersion("1.28.0"))).isTrue();
44+
}
45+
46+
@Test
47+
void greaterThanWithDifferentMajorVersions() {
48+
assertThat(new OTelVersion("2.28.0").isGreaterThan(new OTelVersion("1.28.0"))).isTrue();
49+
}
50+
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.monitor.applicationinsights.spring;
4+
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.boot.autoconfigure.AutoConfigurations;
7+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
8+
9+
public class SpringContextTest {
10+
11+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
12+
13+
// The tests of the Spring Monitor features are done in the spring-cloud-azure-starter-monitor-test Maven module
14+
@Test
15+
void loadContext() {
16+
this.contextRunner.withConfiguration(AutoConfigurations.of(AzureSpringMonitorAutoConfig.class)).run(context -> {
17+
});
18+
}
19+
20+
}

0 commit comments

Comments
 (0)