diff --git a/.github/workflows/build-common.yml b/.github/workflows/build-common.yml index 280dae0ef39..a509052be43 100644 --- a/.github/workflows/build-common.yml +++ b/.github/workflows/build-common.yml @@ -10,7 +10,7 @@ on: env: # set this to : to build and test with an unreleased # version of the azure-monitor-opentelemetry-autoconfigure dependency - AZURE_MONITOR_OPENTELEMETRY_AUTOCONFIGURE_SNAPSHOT: + AZURE_MONITOR_OPENTELEMETRY_AUTOCONFIGURE_SNAPSHOT: Azure/azure-sdk-for-java:main jobs: spotless: diff --git a/settings.gradle.kts b/settings.gradle.kts index ea67d10f977..a550acf1e8b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -78,6 +78,8 @@ hideFromDependabot(":smoke-tests:apps:CoreAndFilter2x") hideFromDependabot(":smoke-tests:apps:CoreAndFilter3x") hideFromDependabot(":smoke-tests:apps:CoreAndFilter3xUsingOld3xAgent") hideFromDependabot(":smoke-tests:apps:CustomDimensions") +hideFromDependabot(":smoke-tests:apps:CustomMeasurements:TestExtension") +hideFromDependabot(":smoke-tests:apps:CustomMeasurements") hideFromDependabot(":smoke-tests:apps:CustomInstrumentation") hideFromDependabot(":smoke-tests:apps:DetectUnexpectedOtelMetrics") hideFromDependabot(":smoke-tests:apps:Diagnostics") diff --git a/smoke-tests/apps/CustomMeasurements/TestExtension/build.gradle.kts b/smoke-tests/apps/CustomMeasurements/TestExtension/build.gradle.kts new file mode 100644 index 00000000000..ddd85bbe531 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/TestExtension/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + java +} + +dependencies { + compileOnly(platform(project(":dependencyManagement"))) + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry:opentelemetry-sdk") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} + +tasks.withType().configureEach { + with(options) { + release.set(8) + compilerArgs.add("-Werror") + compilerArgs.add("-Xlint:-options") + } +} + +tasks.jar { + archiveFileName.set("extension.jar") +} diff --git a/smoke-tests/apps/CustomMeasurements/TestExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/CustomMeasurementsCustomizer.java b/smoke-tests/apps/CustomMeasurements/TestExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/CustomMeasurementsCustomizer.java new file mode 100644 index 00000000000..b21f162fa24 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/TestExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/CustomMeasurementsCustomizer.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestextension; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Value; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.logs.LogRecordProcessor; +import io.opentelemetry.sdk.logs.ReadWriteLogRecord; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import java.util.HashMap; +import java.util.Map; + +public class CustomMeasurementsCustomizer implements AutoConfigurationCustomizerProvider { + + private static final AttributeKey> CUSTOM_MEASUREMENTS = + AttributeKey.valueKey("microsoft.custom_measurements"); + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration + .addTracerProviderCustomizer( + (builder, config) -> builder.addSpanProcessor(new CustomMeasurementsSpanProcessor())) + .addLoggerProviderCustomizer( + (builder, config) -> + builder.addLogRecordProcessor(new CustomMeasurementsLogRecordProcessor())); + } + + private static Value customMeasurements() { + Map> values = new HashMap<>(); + values.put("itemsProcessed", Value.of(42.0)); + values.put("queueDepth", Value.of(7.0)); + return Value.of(values); + } + + private static class CustomMeasurementsSpanProcessor implements SpanProcessor { + + @Override + public void onStart(Context parentContext, ReadWriteSpan span) { + span.setAttribute(CUSTOM_MEASUREMENTS, customMeasurements()); + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(ReadableSpan span) {} + + @Override + public boolean isEndRequired() { + return false; + } + } + + private static class CustomMeasurementsLogRecordProcessor implements LogRecordProcessor { + + @Override + public void onEmit(Context context, ReadWriteLogRecord logRecord) { + logRecord.setAttribute(CUSTOM_MEASUREMENTS, customMeasurements()); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode forceFlush() { + return CompletableResultCode.ofSuccess(); + } + } +} diff --git a/smoke-tests/apps/CustomMeasurements/TestExtension/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/smoke-tests/apps/CustomMeasurements/TestExtension/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider new file mode 100644 index 00000000000..e5808a04149 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/TestExtension/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -0,0 +1 @@ +com.microsoft.applicationinsights.smoketestextension.CustomMeasurementsCustomizer diff --git a/smoke-tests/apps/CustomMeasurements/build.gradle.kts b/smoke-tests/apps/CustomMeasurements/build.gradle.kts new file mode 100644 index 00000000000..08bf2254c33 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("ai.smoke-test-jar") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:2.5.12") + implementation("io.opentelemetry:opentelemetry-api") + compileOnly(project(":smoke-tests:apps:CustomMeasurements:TestExtension")) +} diff --git a/smoke-tests/apps/CustomMeasurements/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/smoke-tests/apps/CustomMeasurements/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java new file mode 100644 index 00000000000..95fe11af514 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/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/CustomMeasurements/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java b/smoke-tests/apps/CustomMeasurements/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java new file mode 100644 index 00000000000..af05a70c235 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestapp; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.logs.Severity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + + @GetMapping("/") + public String root() { + return "OK"; + } + + @GetMapping("/test-custom-measurements") + public String testCustomMeasurements() { + GlobalOpenTelemetry.get() + .getLogsBridge() + .get("custom-measurements-test") + .logRecordBuilder() + .setBody("custom measurements") + .setSeverity(Severity.INFO) + .emit(); + return "Test custom measurements"; + } +} diff --git a/smoke-tests/apps/CustomMeasurements/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/CustomMeasurementsTest.java b/smoke-tests/apps/CustomMeasurements/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/CustomMeasurementsTest.java new file mode 100644 index 00000000000..c71614223f2 --- /dev/null +++ b/smoke-tests/apps/CustomMeasurements/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/CustomMeasurementsTest.java @@ -0,0 +1,80 @@ +// 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_11; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.JAVA_11_OPENJ9; +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; +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; + +import com.microsoft.applicationinsights.smoketest.schemav2.MessageData; +import java.io.File; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +@UseAgent +abstract class CustomMeasurementsTest { + + @RegisterExtension + static final SmokeTestExtension testing = + SmokeTestExtension.builder() + .setAgentExtensionFile(new File("TestExtension/build/libs/extension.jar")) + .build(); + + @Test + @TargetUri("/test-custom-measurements") + void customMeasurementsOnSpanAndLogRecord() throws Exception { + Telemetry telemetry = testing.getTelemetry(0); + assertThat(telemetry.rd.getMeasurements()) + .containsEntry("itemsProcessed", 42.0) + .containsEntry("queueDepth", 7.0); + assertThat(telemetry.rd.getProperties()).doesNotContainKey("microsoft.custom_measurements"); + + List logs = testing.mockedIngestion.getMessageDataInRequest(1); + MessageData log = logs.get(0); + assertThat(log.getMessage()).isEqualTo("custom measurements"); + assertThat(log.getMeasurements()) + .containsEntry("itemsProcessed", 42.0) + .containsEntry("queueDepth", 7.0); + assertThat(log.getProperties()).doesNotContainKey("microsoft.custom_measurements"); + } + + @Environment(JAVA_8) + static class Java8Test extends CustomMeasurementsTest {} + + @Environment(JAVA_8_OPENJ9) + static class Java8OpenJ9Test extends CustomMeasurementsTest {} + + @Environment(JAVA_11) + static class Java11Test extends CustomMeasurementsTest {} + + @Environment(JAVA_11_OPENJ9) + static class Java11OpenJ9Test extends CustomMeasurementsTest {} + + @Environment(JAVA_17) + static class Java17Test extends CustomMeasurementsTest {} + + @Environment(JAVA_17_OPENJ9) + static class Java17OpenJ9Test extends CustomMeasurementsTest {} + + @Environment(JAVA_21) + static class Java21Test extends CustomMeasurementsTest {} + + @Environment(JAVA_21_OPENJ9) + static class Java21OpenJ9Test extends CustomMeasurementsTest {} + + @Environment(JAVA_25) + static class Java25Test extends CustomMeasurementsTest {} + + @Environment(JAVA_25_OPENJ9) + static class Java25OpenJ9Test extends CustomMeasurementsTest {} +} diff --git a/smoke-tests/apps/TelemetryProcessors/build.gradle.kts b/smoke-tests/apps/TelemetryProcessors/build.gradle.kts index 27abf3e2f59..cfff3d97124 100644 --- a/smoke-tests/apps/TelemetryProcessors/build.gradle.kts +++ b/smoke-tests/apps/TelemetryProcessors/build.gradle.kts @@ -8,5 +8,6 @@ dependencies { } // this dependency is needed to make wildfly happy implementation("org.reactivestreams:reactive-streams:1.0.3") - implementation("io.opentelemetry:opentelemetry-api:1.31.0") + + implementation("io.opentelemetry:opentelemetry-api:1.59.0") } diff --git a/smoke-tests/framework/src/main/java/com/microsoft/applicationinsights/smoketest/schemav2/MessageData.java b/smoke-tests/framework/src/main/java/com/microsoft/applicationinsights/smoketest/schemav2/MessageData.java index 096c6cf7cc5..144e475d9e5 100644 --- a/smoke-tests/framework/src/main/java/com/microsoft/applicationinsights/smoketest/schemav2/MessageData.java +++ b/smoke-tests/framework/src/main/java/com/microsoft/applicationinsights/smoketest/schemav2/MessageData.java @@ -17,6 +17,9 @@ public class MessageData extends Domain { /** Backing field for property Properties. */ private ConcurrentMap properties; + /** Backing field for property Measurements. */ + private ConcurrentMap measurements; + /** Initializes a new instance of the MessageData class. */ public MessageData() {} @@ -48,6 +51,14 @@ public ConcurrentMap getProperties() { return this.properties; } + /** Gets the Measurements property. */ + public ConcurrentMap getMeasurements() { + if (this.measurements == null) { + this.measurements = new ConcurrentHashMap<>(); + } + return this.measurements; + } + @Override public String toString() { return "MessageData{" @@ -58,6 +69,8 @@ public String toString() { + severityLevel + ", properties=" + properties + + ", measurements=" + + measurements + '}'; } }