From ba8e9d3d6ce9b50097cfc74531bb77463e944a3b Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 3 Apr 2026 15:36:06 -0700 Subject: [PATCH 01/19] Created a new smoke test to validate Micrometer/ActuatorMetrics instrumentation for Spring Boot 4 --- settings.gradle.kts | 1 + .../build.gradle.kts | 8 +++ .../smoketestapp/SpringBootApp.java | 15 +++++ .../smoketestapp/TestController.java | 31 ++++++++++ .../src/main/resources/application.properties | 1 + .../ActuatorMetricsSpringBoot4Test.java | 57 +++++++++++++++++++ .../resources/applicationinsights.json | 10 ++++ .../src/smokeTest/resources/logback-test.xml | 11 ++++ 8 files changed, 134 insertions(+) create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/applicationinsights.json create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/logback-test.xml diff --git a/settings.gradle.kts b/settings.gradle.kts index 09922f86795..b12ed3d9dce 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -59,6 +59,7 @@ include(":smoke-tests:framework") // TODO (trask) consider un-hiding these and running smoke tests against the latest versions hideFromDependabot(":smoke-tests:apps:ActuatorMetrics") +hideFromDependabot(":smoke-tests:apps:ActuatorMetricsSpringBoot4") hideFromDependabot(":smoke-tests:apps:AutoPerfCounters") hideFromDependabot(":smoke-tests:apps:AzureSdk") hideFromDependabot(":smoke-tests:apps:AzureFunctions") diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts new file mode 100644 index 00000000000..acc5766251a --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("ai.smoke-test-jar") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:4.0.0") + implementation("org.springframework.boot:spring-boot-starter-actuator:4.0.0") +} diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java new file mode 100644 index 00000000000..95fe11af514 --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestapp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootApp { + + public static void main(String[] args) { + SpringApplication.run(SpringBootApp.class, args); + } +} diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java new file mode 100644 index 00000000000..ad4a88c9d3b --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestapp; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + + private final Counter counter; + + public TestController(MeterRegistry meterRegistry) { + this.counter = + Counter.builder("demo.requests.total").tag("endpoint", "test").register(meterRegistry); + } + + @GetMapping("/") + public String root() { + return "OK"; + } + + @GetMapping("/test") + public String test() { + counter.increment(); + return "OK!"; + } +} diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties new file mode 100644 index 00000000000..a33fa659021 --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties @@ -0,0 +1 @@ +management.metrics.export.azuremonitor.instrumentation-key=00000000-0000-0000-0000-000000000000 diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java new file mode 100644 index 00000000000..91c3a1b5e10 --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketest; + +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21; +import static org.assertj.core.api.Assertions.assertThat; + +import com.microsoft.applicationinsights.smoketest.schemav2.Data; +import com.microsoft.applicationinsights.smoketest.schemav2.DataPoint; +import com.microsoft.applicationinsights.smoketest.schemav2.Envelope; +import com.microsoft.applicationinsights.smoketest.schemav2.MetricData; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +@UseAgent +abstract class ActuatorMetricsSpringBoot4Test { + + @RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create(); + + @Test + @TargetUri("/test") + void shouldCaptureCustomMetricRegisteredOnSpringMeterRegistry() throws Exception { + testing.getTelemetry(0); + + List metricItems = + testing.mockedIngestion.waitForItems( + ActuatorMetricsSpringBoot4Test::isTargetMetric, 1, 20, TimeUnit.SECONDS); + + MetricData data = (MetricData) ((Data) metricItems.get(0).getData()).getBaseData(); + List points = data.getMetrics(); + + assertThat(points).anySatisfy(point -> assertThat(point.getName()).isEqualTo("demo_requests_total")); + assertThat(data.getProperties()).containsEntry("endpoint", "test"); + } + + static boolean isTargetMetric(Envelope input) { + if (!input.getData().getBaseType().equals("MetricData")) { + return false; + } + MetricData data = (MetricData) ((Data) input.getData()).getBaseData(); + if (!"test".equals(data.getProperties().get("endpoint"))) { + return false; + } + for (DataPoint point : data.getMetrics()) { + if ("demo_requests_total".equals(point.getName()) && point.getValue() >= 1) { + return true; + } + } + return false; + } + + @Environment(JAVA_21) + static class Java21Test extends ActuatorMetricsSpringBoot4Test {} +} diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/applicationinsights.json b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/applicationinsights.json new file mode 100644 index 00000000000..e08473e0175 --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/applicationinsights.json @@ -0,0 +1,10 @@ +{ + "role": { + "name": "testrolename", + "instance": "testroleinstance" + }, + "sampling": { + "percentage": 100 + }, + "metricIntervalSeconds": 5 +} diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/logback-test.xml b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/logback-test.xml new file mode 100644 index 00000000000..0cbbecd57ce --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/resources/logback-test.xml @@ -0,0 +1,11 @@ + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + From becaf96cd3e4fce754e5eec745437cd9bdccd58f Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 3 Apr 2026 16:00:31 -0700 Subject: [PATCH 02/19] fixed the startup blocker: 1. adding module-local version forcing for Boot 4 logging so the test can reach the intended Micrometer failure path. 2. making one small adjustment so the app boots with a local SimpleMeterRegistry and the test fails exactly on the missing AI metric behavior --- .../ActuatorMetricsSpringBoot4/build.gradle.kts | 7 +++++++ .../smoketestapp/SpringBootApp.java | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index acc5766251a..bfdc4adb511 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -2,6 +2,13 @@ plugins { id("ai.smoke-test-jar") } +configurations.configureEach { + resolutionStrategy.force( + "ch.qos.logback:logback-classic:1.5.21", + "ch.qos.logback:logback-core:1.5.21", + "org.slf4j:slf4j-api:2.0.17") +} + dependencies { implementation("org.springframework.boot:spring-boot-starter-web:4.0.0") implementation("org.springframework.boot:spring-boot-starter-actuator:4.0.0") diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java index 95fe11af514..92b7a53ae28 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java @@ -3,12 +3,27 @@ package com.microsoft.applicationinsights.smoketestapp; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.servlet.ServletWebServerFactory; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringBootApp { + @Bean + public ServletWebServerFactory servletWebServerFactory() { + return new TomcatServletWebServerFactory(); + } + + @Bean + public MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } From 6f01b666ef5ba033a7879ff9e6867fd6937b6a70 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 3 Apr 2026 17:31:50 -0700 Subject: [PATCH 03/19] Fix the incompatible bug due to Spring Boot 4 metrics auto-config rename to org.springframework.boot.micrometer.metrics.autoconfigure.* (different module/package) --- .../micrometer/ai/ActuatorInstrumentation.java | 10 ++++++++-- .../ai/AzureMonitorAutoConfiguration.java | 17 ++++++++++++----- .../ActuatorMetricsSpringBoot4/build.gradle.kts | 9 +++++++++ .../smoketestapp/SpringBootApp.java | 4 ++-- .../src/main/resources/application.properties | 1 - 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java index 5b3bc08c284..50caeef2cfd 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java @@ -16,6 +16,11 @@ public final class ActuatorInstrumentation implements TypeInstrumentation { + private static final String SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION = + "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration"; + private static final String SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION = + "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration"; + @Override public ElementMatcher typeMatcher() { return named("org.springframework.boot.autoconfigure.AutoConfigurationImportSelector"); @@ -36,8 +41,9 @@ public static class GetCandidateConfigurationsAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit(@Advice.Return(readOnly = false) List configurations) { - if (configurations.contains( - "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration")) { + if ((configurations.contains(SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION) + || configurations.contains(SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION)) + && !configurations.contains(AzureMonitorAutoConfiguration.class.getName())) { List configs = new ArrayList<>(configurations.size() + 1); configs.addAll(configurations); // using class reference here so that muzzle will consider it a dependency of this advice diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java index 221d5c2d88b..a0b9b23cb24 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java @@ -4,9 +4,6 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.ai; import io.micrometer.core.instrument.Clock; -import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -15,12 +12,22 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class) +@AutoConfigureBefore( + name = { + "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration", + "org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration" + }) // configure after SimpleMeterRegistry is registered, otherwise SimpleMeterRegistry will be // suppressed by the existence of the MeterRegistry created here, which can alter the spring boot // actuator scraping endpoint behavior (since AzureMonitorMeterRegistry is a delta // StepMeterRegistry, while SimpleMeterRegistry is cumulative) -@AutoConfigureAfter({MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class}) +@AutoConfigureAfter( + name = { + "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration", + "org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration", + "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration", + "org.springframework.boot.micrometer.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration" + }) @ConditionalOnBean(Clock.class) @ConditionalOnClass(AzureMonitorMeterRegistry.class) public class AzureMonitorAutoConfiguration { diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index bfdc4adb511..211b1d1dc3b 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -1,3 +1,5 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { id("ai.smoke-test-jar") } @@ -12,4 +14,11 @@ configurations.configureEach { dependencies { implementation("org.springframework.boot:spring-boot-starter-web:4.0.0") implementation("org.springframework.boot:spring-boot-starter-actuator:4.0.0") + implementation("org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.0") +} + +tasks.named("shadowJar") { + append("META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports") + append( + "META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports") } diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java index 92b7a53ae28..a4ff0769f9c 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java @@ -4,7 +4,7 @@ package com.microsoft.applicationinsights.smoketestapp; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.micrometer.core.instrument.Metrics; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; @@ -21,7 +21,7 @@ public ServletWebServerFactory servletWebServerFactory() { @Bean public MeterRegistry meterRegistry() { - return new SimpleMeterRegistry(); + return Metrics.globalRegistry; } public static void main(String[] args) { diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties index a33fa659021..e69de29bb2d 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties @@ -1 +0,0 @@ -management.metrics.export.azuremonitor.instrumentation-key=00000000-0000-0000-0000-000000000000 From 5ea57a1ad7a435121dea7301c218e226e17138cd Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 3 Apr 2026 18:47:07 -0700 Subject: [PATCH 04/19] Add additional JDK versions for the smoke tests --- .../smoketest/ActuatorMetricsSpringBoot4Test.java | 12 ++++++++++++ .../smoketest/MicrometerTest.java | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java index 91c3a1b5e10..1e2eae63980 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java @@ -4,6 +4,9 @@ package com.microsoft.applicationinsights.smoketest; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21_OPENJ9; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_25; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_25_OPENJ9; import static org.assertj.core.api.Assertions.assertThat; import com.microsoft.applicationinsights.smoketest.schemav2.Data; @@ -54,4 +57,13 @@ static boolean isTargetMetric(Envelope input) { @Environment(JAVA_21) static class Java21Test extends ActuatorMetricsSpringBoot4Test {} + + @Environment(JAVA_21_OPENJ9) + static class Java21OpenJ9Test extends ActuatorMetricsSpringBoot4Test {} + + @Environment(JAVA_25) + static class Java25Test extends ActuatorMetricsSpringBoot4Test {} + + @Environment(JAVA_25_OPENJ9) + static class Java25OpenJ9Test extends ActuatorMetricsSpringBoot4Test {} } diff --git a/smoke-tests/apps/Micrometer/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/MicrometerTest.java b/smoke-tests/apps/Micrometer/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/MicrometerTest.java index 64bd632edfe..a196f1cdb12 100644 --- a/smoke-tests/apps/Micrometer/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/MicrometerTest.java +++ b/smoke-tests/apps/Micrometer/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/MicrometerTest.java @@ -9,6 +9,8 @@ import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_17_OPENJ9; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21_OPENJ9; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_25; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_25_OPENJ9; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_8; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_8_OPENJ9; import static org.assertj.core.api.Assertions.assertThat; @@ -99,4 +101,10 @@ static class Java21Test extends MicrometerTest {} @Environment(JAVA_21_OPENJ9) static class Java21OpenJ9Test extends MicrometerTest {} + + @Environment(JAVA_25) + static class Java25Test extends MicrometerTest {} + + @Environment(JAVA_25_OPENJ9) + static class Java25OpenJ9Test extends MicrometerTest {} } From b304e77ae28a589491731dcbd1ea1d087386da56 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 3 Apr 2026 19:18:43 -0700 Subject: [PATCH 05/19] Remove unused empty file --- .../src/main/resources/application.properties | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/application.properties deleted file mode 100644 index e69de29bb2d..00000000000 From 7f04f1bbc83900fdd9f3bf164dab67ce46c85de9 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 3 Apr 2026 19:48:48 -0700 Subject: [PATCH 06/19] ./gradlew spotlessApply --- .../micrometer/ai/ActuatorInstrumentation.java | 4 ++-- .../apps/ActuatorMetricsSpringBoot4/build.gradle.kts | 10 +++++----- .../smoketest/ActuatorMetricsSpringBoot4Test.java | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java index 50caeef2cfd..f5440e97ba5 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java @@ -17,9 +17,9 @@ public final class ActuatorInstrumentation implements TypeInstrumentation { private static final String SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION = - "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration"; + "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration"; private static final String SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION = - "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration"; + "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration"; @Override public ElementMatcher typeMatcher() { diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index 211b1d1dc3b..c7bced36838 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -6,9 +6,10 @@ plugins { configurations.configureEach { resolutionStrategy.force( - "ch.qos.logback:logback-classic:1.5.21", - "ch.qos.logback:logback-core:1.5.21", - "org.slf4j:slf4j-api:2.0.17") + "ch.qos.logback:logback-classic:1.5.21", + "ch.qos.logback:logback-core:1.5.21", + "org.slf4j:slf4j-api:2.0.17" + ) } dependencies { @@ -19,6 +20,5 @@ dependencies { tasks.named("shadowJar") { append("META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports") - append( - "META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports") + append("META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports") } diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java index 1e2eae63980..08ba8b9d492 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java @@ -35,7 +35,8 @@ void shouldCaptureCustomMetricRegisteredOnSpringMeterRegistry() throws Exception MetricData data = (MetricData) ((Data) metricItems.get(0).getData()).getBaseData(); List points = data.getMetrics(); - assertThat(points).anySatisfy(point -> assertThat(point.getName()).isEqualTo("demo_requests_total")); + assertThat(points) + .anySatisfy(point -> assertThat(point.getName()).isEqualTo("demo_requests_total")); assertThat(data.getProperties()).containsEntry("endpoint", "test"); } From 827a7b1097f889d2f882588211cd6a2fa6cca659 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Tue, 7 Apr 2026 18:20:44 -0700 Subject: [PATCH 07/19] Added Java 17 in smoke tests. Minimum supported Java version by Spring Boot 4.0 is Java 17. https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide --- .../smoketest/ActuatorMetricsSpringBoot4Test.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java index 08ba8b9d492..afd272a8942 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java @@ -3,6 +3,8 @@ package com.microsoft.applicationinsights.smoketest; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_17; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_17_OPENJ9; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_21_OPENJ9; import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_25; @@ -56,6 +58,12 @@ static boolean isTargetMetric(Envelope input) { return false; } + @Environment(JAVA_17) + static class Java17Test extends ActuatorMetricsSpringBoot4Test {} + + @Environment(JAVA_17_OPENJ9) + static class Java17OpenJ9Test extends ActuatorMetricsSpringBoot4Test {} + @Environment(JAVA_21) static class Java21Test extends ActuatorMetricsSpringBoot4Test {} From 70c46a22e5aeb6b84e36de34902f42e264a21728 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Thu, 9 Apr 2026 16:18:42 -0700 Subject: [PATCH 08/19] revert fix --- .../micrometer/ai/ActuatorInstrumentation.java | 10 ++-------- .../ai/AzureMonitorAutoConfiguration.java | 17 +++++------------ 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java index f5440e97ba5..5b3bc08c284 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java @@ -16,11 +16,6 @@ public final class ActuatorInstrumentation implements TypeInstrumentation { - private static final String SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION = - "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration"; - private static final String SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION = - "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration"; - @Override public ElementMatcher typeMatcher() { return named("org.springframework.boot.autoconfigure.AutoConfigurationImportSelector"); @@ -41,9 +36,8 @@ public static class GetCandidateConfigurationsAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit(@Advice.Return(readOnly = false) List configurations) { - if ((configurations.contains(SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION) - || configurations.contains(SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION)) - && !configurations.contains(AzureMonitorAutoConfiguration.class.getName())) { + if (configurations.contains( + "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration")) { List configs = new ArrayList<>(configurations.size() + 1); configs.addAll(configurations); // using class reference here so that muzzle will consider it a dependency of this advice diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java index a0b9b23cb24..221d5c2d88b 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java @@ -4,6 +4,9 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.ai; import io.micrometer.core.instrument.Clock; +import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -12,22 +15,12 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@AutoConfigureBefore( - name = { - "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration", - "org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration" - }) +@AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class) // configure after SimpleMeterRegistry is registered, otherwise SimpleMeterRegistry will be // suppressed by the existence of the MeterRegistry created here, which can alter the spring boot // actuator scraping endpoint behavior (since AzureMonitorMeterRegistry is a delta // StepMeterRegistry, while SimpleMeterRegistry is cumulative) -@AutoConfigureAfter( - name = { - "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration", - "org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration", - "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration", - "org.springframework.boot.micrometer.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration" - }) +@AutoConfigureAfter({MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class}) @ConditionalOnBean(Clock.class) @ConditionalOnClass(AzureMonitorMeterRegistry.class) public class AzureMonitorAutoConfiguration { From 25be67be0528bc89015b036815b1fa7a33dfb818 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Mon, 20 Apr 2026 16:10:25 -0700 Subject: [PATCH 09/19] Update smoke test so that it misses the metrics when reverting the fix --- .../ActuatorMetricsSpringBoot4/build.gradle.kts | 6 +++++- .../smoketestapp/SpringBootApp.java | 7 ------- .../smoketestapp/TestController.java | 14 +++++++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index c7bced36838..fd656aba8b7 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -18,7 +18,11 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.0") } +// Spring Boot 4 splits auto-configuration across many module JARs, each with its own +// META-INF/spring/AutoConfiguration.imports file. Shadow's default behavior keeps only +// one copy, losing most entries. We provide a pre-merged resource file in +// src/main/resources/META-INF/spring/ and use append() to prevent any single +// dependency copy from overwriting it. tasks.named("shadowJar") { append("META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports") - append("META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports") } diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java index a4ff0769f9c..cc12bb0d8b8 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java @@ -3,8 +3,6 @@ package com.microsoft.applicationinsights.smoketestapp; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Metrics; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; @@ -19,11 +17,6 @@ public ServletWebServerFactory servletWebServerFactory() { return new TomcatServletWebServerFactory(); } - @Bean - public MeterRegistry meterRegistry() { - return Metrics.globalRegistry; - } - public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java index ad4a88c9d3b..7c285dc56b2 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java @@ -5,6 +5,7 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -13,9 +14,14 @@ public class TestController { private final Counter counter; - public TestController(MeterRegistry meterRegistry) { + public TestController(ObjectProvider meterRegistryProvider) { + MeterRegistry meterRegistry = meterRegistryProvider.getIfAvailable(); this.counter = - Counter.builder("demo.requests.total").tag("endpoint", "test").register(meterRegistry); + meterRegistry == null + ? null + : Counter.builder("demo.requests.total") + .tag("endpoint", "test") + .register(meterRegistry); } @GetMapping("/") @@ -25,7 +31,9 @@ public String root() { @GetMapping("/test") public String test() { - counter.increment(); + if (counter != null) { + counter.increment(); + } return "OK!"; } } From ea12d345d3a0dafb4b1663517ad91577eb3593d2 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Mon, 20 Apr 2026 16:35:16 -0700 Subject: [PATCH 10/19] Revert "revert fix" This reverts commit 70c46a22e5aeb6b84e36de34902f42e264a21728. --- .../micrometer/ai/ActuatorInstrumentation.java | 10 ++++++++-- .../ai/AzureMonitorAutoConfiguration.java | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java index 5b3bc08c284..f5440e97ba5 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java @@ -16,6 +16,11 @@ public final class ActuatorInstrumentation implements TypeInstrumentation { + private static final String SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION = + "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration"; + private static final String SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION = + "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration"; + @Override public ElementMatcher typeMatcher() { return named("org.springframework.boot.autoconfigure.AutoConfigurationImportSelector"); @@ -36,8 +41,9 @@ public static class GetCandidateConfigurationsAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit(@Advice.Return(readOnly = false) List configurations) { - if (configurations.contains( - "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration")) { + if ((configurations.contains(SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION) + || configurations.contains(SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION)) + && !configurations.contains(AzureMonitorAutoConfiguration.class.getName())) { List configs = new ArrayList<>(configurations.size() + 1); configs.addAll(configurations); // using class reference here so that muzzle will consider it a dependency of this advice diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java index 221d5c2d88b..a0b9b23cb24 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/AzureMonitorAutoConfiguration.java @@ -4,9 +4,6 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.ai; import io.micrometer.core.instrument.Clock; -import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -15,12 +12,22 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class) +@AutoConfigureBefore( + name = { + "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration", + "org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration" + }) // configure after SimpleMeterRegistry is registered, otherwise SimpleMeterRegistry will be // suppressed by the existence of the MeterRegistry created here, which can alter the spring boot // actuator scraping endpoint behavior (since AzureMonitorMeterRegistry is a delta // StepMeterRegistry, while SimpleMeterRegistry is cumulative) -@AutoConfigureAfter({MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class}) +@AutoConfigureAfter( + name = { + "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration", + "org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration", + "org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration", + "org.springframework.boot.micrometer.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration" + }) @ConditionalOnBean(Clock.class) @ConditionalOnClass(AzureMonitorMeterRegistry.class) public class AzureMonitorAutoConfiguration { From 96f072610c2187ae75da417c98569c816d693988 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Mon, 20 Apr 2026 17:29:53 -0700 Subject: [PATCH 11/19] add missed file for auto-config classes --- ...ot.autoconfigure.AutoConfiguration.imports | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..4a82f65e196 --- /dev/null +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,87 @@ +org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration +org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.endpoint.jackson.Jackson2EndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration +org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.management.HeapDumpWebEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.sbom.SbomEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration +org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration +org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration +org.springframework.boot.autoconfigure.aop.AopAutoConfiguration +org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration +org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration +org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration +org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration +org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration +org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration +org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration +org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration +org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration +org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration +org.springframework.boot.health.autoconfigure.actuate.endpoint.AvailabilityProbesAutoConfiguration +org.springframework.boot.health.autoconfigure.actuate.endpoint.HealthEndpointAutoConfiguration +org.springframework.boot.health.autoconfigure.application.AvailabilityHealthContributorAutoConfiguration +org.springframework.boot.health.autoconfigure.application.DiskSpaceHealthContributorAutoConfiguration +org.springframework.boot.health.autoconfigure.application.SslHealthContributorAutoConfiguration +org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration +org.springframework.boot.health.autoconfigure.registry.HealthContributorRegistryAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAspectsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.MetricsEndpointAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.appoptics.AppOpticsMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.atlas.AtlasMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.datadog.DatadogMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.dynatrace.DynatraceMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.elastic.ElasticMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.ganglia.GangliaMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.graphite.GraphiteMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.humio.HumioMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.influx.InfluxMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.jmx.JmxMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.kairos.KairosMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.newrelic.NewRelicMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp.OtlpMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.prometheus.PrometheusMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.stackdriver.StackdriverMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.export.statsd.StatsdMetricsExportAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.jvm.JvmMetricsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.logging.log4j2.Log4J2MetricsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.logging.logback.LogbackMetricsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.ssl.SslMetricsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.startup.StartupTimeMetricsListenerAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.system.SystemMetricsAutoConfiguration +org.springframework.boot.micrometer.metrics.autoconfigure.task.TaskExecutorMetricsAutoConfiguration +org.springframework.boot.servlet.autoconfigure.HttpEncodingAutoConfiguration +org.springframework.boot.servlet.autoconfigure.MultipartAutoConfiguration +org.springframework.boot.servlet.autoconfigure.actuate.web.exchanges.ServletHttpExchangesAutoConfiguration +org.springframework.boot.servlet.autoconfigure.actuate.web.mappings.ServletMappingsAutoConfiguration +org.springframework.boot.servlet.autoconfigure.actuate.web.ServletManagementContextAutoConfiguration +org.springframework.boot.tomcat.autoconfigure.actuate.web.server.TomcatReactiveManagementContextAutoConfiguration +org.springframework.boot.tomcat.autoconfigure.actuate.web.server.TomcatServletManagementContextAutoConfiguration +org.springframework.boot.tomcat.autoconfigure.metrics.TomcatMetricsAutoConfiguration +org.springframework.boot.tomcat.autoconfigure.reactive.TomcatReactiveWebServerAutoConfiguration +org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration +org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration +org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration +org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAutoConfiguration +org.springframework.boot.webmvc.autoconfigure.actuate.endpoint.web.WebMvcHealthEndpointExtensionAutoConfiguration +org.springframework.boot.webmvc.autoconfigure.actuate.web.mappings.WebMvcMappingsAutoConfiguration +org.springframework.boot.webmvc.autoconfigure.error.ErrorMvcAutoConfiguration From e413d43be4de271b7751133cdfb96959bb6323c7 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Tue, 21 Apr 2026 20:43:23 -0700 Subject: [PATCH 12/19] simplify --- .../build.gradle.kts | 19 ++-- ...ot.autoconfigure.AutoConfiguration.imports | 87 ------------------- 2 files changed, 9 insertions(+), 97 deletions(-) delete mode 100644 smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index fd656aba8b7..d5455f19126 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -1,9 +1,13 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.microsoft.applicationinsights.gradle.AiSmokeTestExtension plugins { - id("ai.smoke-test-jar") + id("ai.smoke-test") + id("org.springframework.boot") version "4.0.0" } +// Override the workspace-wide dependencyManagement pin of logback 1.3.x (Java 8 target) +// so Spring Boot 4 can resolve its required logback 1.5.x (requires Java 17, which this +// app already targets). configurations.configureEach { resolutionStrategy.force( "ch.qos.logback:logback-classic:1.5.21", @@ -18,11 +22,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.0") } -// Spring Boot 4 splits auto-configuration across many module JARs, each with its own -// META-INF/spring/AutoConfiguration.imports file. Shadow's default behavior keeps only -// one copy, losing most entries. We provide a pre-merged resource file in -// src/main/resources/META-INF/spring/ and use append() to prevent any single -// dependency copy from overwriting it. -tasks.named("shadowJar") { - append("META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports") -} +val aiSmokeTest = extensions.getByType(AiSmokeTestExtension::class) +aiSmokeTest.testAppArtifactDir.set(tasks.bootJar.flatMap { it.destinationDirectory }) +aiSmokeTest.testAppArtifactFilename.set(tasks.bootJar.flatMap { it.archiveFileName }) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 4a82f65e196..00000000000 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,87 +0,0 @@ -org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration -org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.endpoint.jackson.Jackson2EndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration -org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.management.HeapDumpWebEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.sbom.SbomEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration -org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration -org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration -org.springframework.boot.autoconfigure.aop.AopAutoConfiguration -org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration -org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration -org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration -org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration -org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration -org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration -org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration -org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration -org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration -org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration -org.springframework.boot.health.autoconfigure.actuate.endpoint.AvailabilityProbesAutoConfiguration -org.springframework.boot.health.autoconfigure.actuate.endpoint.HealthEndpointAutoConfiguration -org.springframework.boot.health.autoconfigure.application.AvailabilityHealthContributorAutoConfiguration -org.springframework.boot.health.autoconfigure.application.DiskSpaceHealthContributorAutoConfiguration -org.springframework.boot.health.autoconfigure.application.SslHealthContributorAutoConfiguration -org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration -org.springframework.boot.health.autoconfigure.registry.HealthContributorRegistryAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAspectsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.MetricsEndpointAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.appoptics.AppOpticsMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.atlas.AtlasMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.datadog.DatadogMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.dynatrace.DynatraceMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.elastic.ElasticMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.ganglia.GangliaMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.graphite.GraphiteMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.humio.HumioMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.influx.InfluxMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.jmx.JmxMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.kairos.KairosMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.newrelic.NewRelicMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp.OtlpMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.prometheus.PrometheusMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.stackdriver.StackdriverMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.export.statsd.StatsdMetricsExportAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.jvm.JvmMetricsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.logging.log4j2.Log4J2MetricsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.logging.logback.LogbackMetricsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.ssl.SslMetricsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.startup.StartupTimeMetricsListenerAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.system.SystemMetricsAutoConfiguration -org.springframework.boot.micrometer.metrics.autoconfigure.task.TaskExecutorMetricsAutoConfiguration -org.springframework.boot.servlet.autoconfigure.HttpEncodingAutoConfiguration -org.springframework.boot.servlet.autoconfigure.MultipartAutoConfiguration -org.springframework.boot.servlet.autoconfigure.actuate.web.exchanges.ServletHttpExchangesAutoConfiguration -org.springframework.boot.servlet.autoconfigure.actuate.web.mappings.ServletMappingsAutoConfiguration -org.springframework.boot.servlet.autoconfigure.actuate.web.ServletManagementContextAutoConfiguration -org.springframework.boot.tomcat.autoconfigure.actuate.web.server.TomcatReactiveManagementContextAutoConfiguration -org.springframework.boot.tomcat.autoconfigure.actuate.web.server.TomcatServletManagementContextAutoConfiguration -org.springframework.boot.tomcat.autoconfigure.metrics.TomcatMetricsAutoConfiguration -org.springframework.boot.tomcat.autoconfigure.reactive.TomcatReactiveWebServerAutoConfiguration -org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration -org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration -org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration -org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAutoConfiguration -org.springframework.boot.webmvc.autoconfigure.actuate.endpoint.web.WebMvcHealthEndpointExtensionAutoConfiguration -org.springframework.boot.webmvc.autoconfigure.actuate.web.mappings.WebMvcMappingsAutoConfiguration -org.springframework.boot.webmvc.autoconfigure.error.ErrorMvcAutoConfiguration From d97cbfc3e42505b10a042248034f4838f96f476b Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Tue, 21 Apr 2026 20:50:16 -0700 Subject: [PATCH 13/19] simplify --- .../smoketestapp/TestController.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java index 7c285dc56b2..ad4a88c9d3b 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java @@ -5,7 +5,6 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -14,14 +13,9 @@ public class TestController { private final Counter counter; - public TestController(ObjectProvider meterRegistryProvider) { - MeterRegistry meterRegistry = meterRegistryProvider.getIfAvailable(); + public TestController(MeterRegistry meterRegistry) { this.counter = - meterRegistry == null - ? null - : Counter.builder("demo.requests.total") - .tag("endpoint", "test") - .register(meterRegistry); + Counter.builder("demo.requests.total").tag("endpoint", "test").register(meterRegistry); } @GetMapping("/") @@ -31,9 +25,7 @@ public String root() { @GetMapping("/test") public String test() { - if (counter != null) { - counter.increment(); - } + counter.increment(); return "OK!"; } } From 5b3ec5d800d13fc2d0f89d0a7c1dbf6b19851c49 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Tue, 21 Apr 2026 20:50:58 -0700 Subject: [PATCH 14/19] comment --- .../instrumentation/micrometer/ai/ActuatorInstrumentation.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java index f5440e97ba5..b6bf0146a7e 100644 --- a/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java +++ b/agent/instrumentation/micrometer-1.0/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/ai/ActuatorInstrumentation.java @@ -41,6 +41,8 @@ public static class GetCandidateConfigurationsAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit(@Advice.Return(readOnly = false) List configurations) { + // guard against re-adding AzureMonitorAutoConfiguration if this advice runs more than once + // on the same list (e.g. nested/repeated invocations of getCandidateConfigurations) if ((configurations.contains(SPRING_BOOT_3_METRICS_AUTO_CONFIGURATION) || configurations.contains(SPRING_BOOT_4_METRICS_AUTO_CONFIGURATION)) && !configurations.contains(AzureMonitorAutoConfiguration.class.getName())) { From 8a6479fa929b9b9291e305a899ef50ec66e99721 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 22 Apr 2026 08:53:14 -0700 Subject: [PATCH 15/19] comment --- .../apps/ActuatorMetricsSpringBoot4/build.gradle.kts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index d5455f19126..1a3a98b9285 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -5,9 +5,12 @@ plugins { id("org.springframework.boot") version "4.0.0" } -// Override the workspace-wide dependencyManagement pin of logback 1.3.x (Java 8 target) -// so Spring Boot 4 can resolve its required logback 1.5.x (requires Java 17, which this -// app already targets). +// The workspace-wide dependencyManagement module pins logback-classic to 1.3.16 (Java 8 +// compatible). Spring Boot 4 starters don't declare a logback version directly (they rely +// on Spring Boot's BOM, which we don't apply here -- applying io.spring.dependency-management +// would also downgrade Jetty used by the smoke-test framework), so the 1.3.16 constraint +// wins and fails at startup with AbstractMethodError against the SB4 RootLogLevelConfigurator. +// Force the logback 1.5.x line required by Spring Boot 4. configurations.configureEach { resolutionStrategy.force( "ch.qos.logback:logback-classic:1.5.21", From 767f2de4e65e2cf2b2f25577d50b39be3bec60c5 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 22 Apr 2026 08:59:32 -0700 Subject: [PATCH 16/19] simplify --- .../applicationinsights/smoketestapp/SpringBootApp.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java index cc12bb0d8b8..95fe11af514 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java @@ -5,18 +5,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; -import org.springframework.boot.web.server.servlet.ServletWebServerFactory; -import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringBootApp { - @Bean - public ServletWebServerFactory servletWebServerFactory() { - return new TomcatServletWebServerFactory(); - } - public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } From 9ff9ff8ecbc8d7935288183aee8d4f8f8b1d9a85 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 22 Apr 2026 09:08:30 -0700 Subject: [PATCH 17/19] comment --- .../apps/ActuatorMetricsSpringBoot4/build.gradle.kts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index 1a3a98b9285..f2e663f4c03 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -5,12 +5,11 @@ plugins { id("org.springframework.boot") version "4.0.0" } -// The workspace-wide dependencyManagement module pins logback-classic to 1.3.16 (Java 8 -// compatible). Spring Boot 4 starters don't declare a logback version directly (they rely -// on Spring Boot's BOM, which we don't apply here -- applying io.spring.dependency-management -// would also downgrade Jetty used by the smoke-test framework), so the 1.3.16 constraint -// wins and fails at startup with AbstractMethodError against the SB4 RootLogLevelConfigurator. -// Force the logback 1.5.x line required by Spring Boot 4. +// The ai.smoke-test convention plugin applies `resolutionStrategy.force` on every +// configuration to pin logback-classic to 1.2.12 and slf4j-api to 1.7.36 (needed by the +// older Spring Boot smoke-test apps). Spring Boot 4 requires logback 1.5.x and slf4j 2.x +// and fails at startup with AbstractMethodError against SB4's RootLogLevelConfigurator +// otherwise. Re-force the newer versions here to override the convention's force. configurations.configureEach { resolutionStrategy.force( "ch.qos.logback:logback-classic:1.5.21", From 500f93460648e93a97a567181d2490b5d36414d0 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 22 Apr 2026 09:19:45 -0700 Subject: [PATCH 18/19] Match ActuatorMetricsSpringBoot4 target metric on counter name only Match on counter name only so that a regression in tag -> property propagation surfaces as a clear assertion failure on the properties check in the test body rather than a waitForItems timeout. --- .../smoketest/ActuatorMetricsSpringBoot4Test.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java index afd272a8942..c7f81d0a3fb 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/ActuatorMetricsSpringBoot4Test.java @@ -47,9 +47,6 @@ static boolean isTargetMetric(Envelope input) { return false; } MetricData data = (MetricData) ((Data) input.getData()).getBaseData(); - if (!"test".equals(data.getProperties().get("endpoint"))) { - return false; - } for (DataPoint point : data.getMetrics()) { if ("demo_requests_total".equals(point.getName()) && point.getValue() >= 1) { return true; From 7537ec9774ff76b5e5597dfece7f6b9fbb1200b1 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 22 Apr 2026 09:20:34 -0700 Subject: [PATCH 19/19] Force slf4j bridges to 2.x in ActuatorMetricsSpringBoot4 The ai.smoke-test convention forces log4j-over-slf4j, jcl-over-slf4j, and jul-to-slf4j to 1.7.36. Spring Boot 4 can transitively pull these bridges, leaving them binary-incompatible with the slf4j-api 2.0.17 this module already forces. Re-force the bridges to 2.0.17 to match. --- .../ActuatorMetricsSpringBoot4/build.gradle.kts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts index f2e663f4c03..8bfad2ccabc 100644 --- a/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts +++ b/smoke-tests/apps/ActuatorMetricsSpringBoot4/build.gradle.kts @@ -6,15 +6,20 @@ plugins { } // The ai.smoke-test convention plugin applies `resolutionStrategy.force` on every -// configuration to pin logback-classic to 1.2.12 and slf4j-api to 1.7.36 (needed by the -// older Spring Boot smoke-test apps). Spring Boot 4 requires logback 1.5.x and slf4j 2.x +// configuration to pin logback-classic to 1.2.12 and the slf4j bridges (slf4j-api, +// log4j-over-slf4j, jcl-over-slf4j, jul-to-slf4j) to 1.7.36 (needed by the older +// Spring Boot smoke-test apps). Spring Boot 4 requires logback 1.5.x and slf4j 2.x // and fails at startup with AbstractMethodError against SB4's RootLogLevelConfigurator -// otherwise. Re-force the newer versions here to override the convention's force. +// otherwise. Re-force the newer versions here to override the convention's force, +// including all slf4j bridges so they stay binary-compatible with slf4j-api 2.x. configurations.configureEach { resolutionStrategy.force( "ch.qos.logback:logback-classic:1.5.21", "ch.qos.logback:logback-core:1.5.21", - "org.slf4j:slf4j-api:2.0.17" + "org.slf4j:slf4j-api:2.0.17", + "org.slf4j:log4j-over-slf4j:2.0.17", + "org.slf4j:jcl-over-slf4j:2.0.17", + "org.slf4j:jul-to-slf4j:2.0.17" ) }