diff --git a/.github/repository-settings.md b/.github/repository-settings.md index b2103ad48669..17879d5d1d8a 100644 --- a/.github/repository-settings.md +++ b/.github/repository-settings.md @@ -1,117 +1,8 @@ # Repository settings This document describes any changes that have been made to the -settings for this repository beyond the [OpenTelemetry default repository -settings](https://github.com/open-telemetry/community/blob/main/docs/how-to-configure-new-repository.md#repository-settings). - -## General > Pull Requests - -- Allow squash merging > Default to pull request title - -- Allow auto-merge - -## Actions > General - -- Fork pull request workflows from outside collaborators: - "Require approval for first-time contributors who are new to GitHub" - - (To reduce friction for new contributors, - as the default is "Require approval for first-time contributors") - -- Workflow permissions - - Default permissions granted to the `GITHUB_TOKEN` when running workflows in this repository: - Read repository contents and packages permissions - - Allow GitHub Actions to create and approve pull requests: UNCHECKED - -## Rules > Rulesets - -### `main` and release branches - -- Targeted branches: - - `main` - - `release/*` -- Branch rules - - Restrict deletions: CHECKED - - Require a pull request before merging: CHECKED - - Required approvals: 1 - - Require review from Code Owners: CHECKED - - Allowed merge methods: Squash - - Require status checks to pass - - Do not require status checks on creation: CHECKED - - Status checks that are required - - EasyCLA - - `required-status-check` - - `gradle-wrapper-validation` - - Block force pushes: CHECKED - - Require code scanning results: CHECKED - - CodeQL - - Security alerts: High or higher - - Alerts: Errors - -> [!NOTE] -> This repository can't "require linear history" because there is an old merge commit on `main` -> (and so also on the release branches). - -### `cloudfoundry` branch - -- Targeted branches: - - `cloudfoundry` -- Branch rules - - Restrict deletions: CHECKED - - Require linear history: CHECKED - - Require a pull request before merging: CHECKED - - Required approvals: 1 - - Require review from Code Owners: CHECKED - - Allowed merge methods: Squash - - Require status checks to pass - - EasyCLA - - Block force pushes: CHECKED - -### `gh-pages` branch - -- Targeted branches: - - `gh-pages` -- Branch rules - - Restrict deletions: CHECKED - - Require linear history: CHECKED - - Block force pushes: CHECKED - -### Old-style release branches - -- Targeted branches: - - `v0.*` - - `v1.*` -- Branch rules - - Restrict creations: CHECKED - - Restrict updates: CHECKED - - Restrict deletions: CHECKED - -### Restrict branch creation - -- Targeted branches - - Exclude: - - `release/*` - - `renovate/**/*` - - `otelbot/**/*` - - `revert-*/**/*` (these are created when using the GitHub UI to revert a PR) -- Restrict creations: CHECKED - -### Restrict updating tags - -- Targeted tags - - All tags -- Restrict updates: CHECKED -- Restrict deletions: CHECKED - -## Branch protections - -### `main`, `release/*`, `cloudfoundry` - -- Restrict who can push to matching branches: CHECKED - -## Code security and analysis - -- Secret scanning: Enabled +settings in this repository outside the settings tracked in the +private admin repo. ## Secrets and variables > Actions diff --git a/CHANGELOG.md b/CHANGELOG.md index 480744fd8291..7e2adb17bc52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,33 @@ ## Unreleased +### Migration notes +- Tomcat metrics definitions provided by JMX Metric Insight subsystem + - metric `http.server.tomcat.errorCount` --> `tomcat.error.count` + - attribute: `name` --> `tomcat.request.processor.name` + - type: Gauge --> Counter + - metric `http.server.tomcat.requestCount` --> `tomcat.request.count` + - attribute: `name` --> `tomcat.request.processor.name` + - type: Gauge --> Counter + - metric `http.server.tomcat.maxTime` --> `tomcat.request.duration.max` + - attribute: `name` --> `tomcat.request.processor.name` + - unit: `ms` --> `s` + - metric `http.server.tomcat.processingTime` --> `tomcat.request.duration.sum` + - attribute: `name` --> `tomcat.request.processor.name` + - unit: `ms` --> `s` + - metric `http.server.tomcat.traffic` --> `tomcat.network.io` + - attribute: `name` --> `tomcat.request.processor.name`, `direction` --> `network.io.direction` + - metric `http.server.tomcat.sessions.activeSessions` --> `tomcat.session.active.count` + - attribute: `context` --> `tomcat.context` + - metric `http.server.tomcat.threads` split into two metrics: `tomcat.thread.count` and `tomcat.thread.busy.count` + - attribute: `name` --> `tomcat.thread.pool.name`, `state` removed + +### 📈 Enhancements + +- **JMX Metric Insight**: improved Tomcat metrics alignment to semconv (see Migration notes above for details) and added new Tomcat metrics: `tomcat.session.active.limit`, `tomcat.thread.limit` + ([#13650](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/13650)) + + ## Version 2.16.0 (2025-05-15) ### ⚠️⚠️ Breaking changes ⚠️⚠️ @@ -11,7 +38,6 @@ - Remove deprecated property for disabling kafka metrics ([#13803](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/13803)) - ### 🌟 New javaagent instrumentation - Add Avaje Jex Instrumentation diff --git a/benchmark-overhead/build.gradle.kts b/benchmark-overhead/build.gradle.kts index 153f4a5b6a5c..5084c0959804 100644 --- a/benchmark-overhead/build.gradle.kts +++ b/benchmark-overhead/build.gradle.kts @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation(enforcedPlatform("org.junit:junit-bom:5.12.2")) + implementation(enforcedPlatform("org.junit:junit-bom:5.13.0")) testImplementation("org.testcontainers:testcontainers:1.21.1") testImplementation("org.testcontainers:postgresql:1.21.1") diff --git a/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties b/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties index 9128c7d428d8..3735f265b953 100644 --- a/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties +++ b/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/conventions/build.gradle.kts b/conventions/build.gradle.kts index 5e608633cc4f..39e8aae6837f 100644 --- a/conventions/build.gradle.kts +++ b/conventions/build.gradle.kts @@ -71,7 +71,7 @@ dependencies { implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.2.0") implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.6") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.12.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.0")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testImplementation("org.assertj:assertj-core:3.27.3") diff --git a/conventions/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts b/conventions/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts index 87d055055b31..cea2510b7ae6 100644 --- a/conventions/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts @@ -7,7 +7,7 @@ plugins { } dependencies { - add("signature", "com.toasttab.android:gummy-bears-api-21:0.3.0:coreLib@signature") + add("signature", "com.toasttab.android:gummy-bears-api-21:0.12.0:coreLib@signature") } animalsniffer { diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index f2b6dd9350b3..6ed3c2da511c 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -81,7 +81,7 @@ val CORE_DEPENDENCIES = listOf( // There are dependencies included here that appear to have no usages, but are maintained at // this top level to help consistently satisfy large numbers of transitive dependencies. val DEPENDENCIES = listOf( - "org.junit.jupiter:junit-jupiter-api:5.12.2", + "org.junit.jupiter:junit-jupiter-api:5.13.0", "org.spockframework:spock-core:2.4-M6-groovy-4.0", "org.spockframework:spock-junit4:2.4-M6-groovy-4.0", diff --git a/examples/distro/build.gradle b/examples/distro/build.gradle index b09efd1f8c56..35dcb9ebf8ea 100644 --- a/examples/distro/build.gradle +++ b/examples/distro/build.gradle @@ -69,7 +69,7 @@ subprojects { testImplementation("org.mockito:mockito-core:5.18.0") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.12.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.0")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/examples/distro/gradle/wrapper/gradle-wrapper.properties b/examples/distro/gradle/wrapper/gradle-wrapper.properties index 9128c7d428d8..3735f265b953 100644 --- a/examples/distro/gradle/wrapper/gradle-wrapper.properties +++ b/examples/distro/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/extension/build.gradle b/examples/extension/build.gradle index b6eaf99faae3..06571d17bb98 100644 --- a/examples/extension/build.gradle +++ b/examples/extension/build.gradle @@ -104,7 +104,7 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-api") testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.12.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.0")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/examples/extension/gradle/wrapper/gradle-wrapper.properties b/examples/extension/gradle/wrapper/gradle-wrapper.properties index 9128c7d428d8..3735f265b953 100644 --- a/examples/extension/gradle/wrapper/gradle-wrapper.properties +++ b/examples/extension/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradle-plugins/build.gradle.kts b/gradle-plugins/build.gradle.kts index d25fd396a6c6..b1dd94056de2 100644 --- a/gradle-plugins/build.gradle.kts +++ b/gradle-plugins/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { testImplementation("org.assertj:assertj-core:3.27.3") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.12.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.0")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties index 9128c7d428d8..3735f265b953 100644 --- a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties +++ b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9128c7d428d8..3735f265b953 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/semconv/http/ValidRequestMethodsProvider.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/semconv/http/ValidRequestMethodsProvider.java index f7b5a56775fb..4d7ed62363c6 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/semconv/http/ValidRequestMethodsProvider.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/semconv/http/ValidRequestMethodsProvider.java @@ -10,11 +10,13 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.support.ParameterDeclarations; final class ValidRequestMethodsProvider implements ArgumentsProvider { @Override - public Stream provideArguments(ExtensionContext context) { + public Stream provideArguments( + ParameterDeclarations parameters, ExtensionContext context) { return HttpConstants.KNOWN_METHODS.stream().map(Arguments::of); } } diff --git a/instrumentation-docs/build.gradle.kts b/instrumentation-docs/build.gradle.kts index b733a3ce8d8a..a1360f5be49a 100644 --- a/instrumentation-docs/build.gradle.kts +++ b/instrumentation-docs/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.0") implementation("io.opentelemetry:opentelemetry-sdk-common") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.12.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.0")) testImplementation("org.assertj:assertj-core:3.27.3") testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") @@ -22,6 +22,7 @@ tasks { val runAnalysis by registering(JavaExec::class) { dependsOn(classes) + systemProperty("basePath", project.rootDir) mainClass.set("io.opentelemetry.instrumentation.docs.DocGeneratorApplication") classpath(sourceSets["main"].runtimeClasspath) } diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/DocGeneratorApplication.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/DocGeneratorApplication.java index 44a38ab8bea9..adf17c29cfcd 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/DocGeneratorApplication.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/DocGeneratorApplication.java @@ -7,13 +7,11 @@ import static java.util.Locale.Category.FORMAT; -import com.fasterxml.jackson.core.JsonProcessingException; import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule; import io.opentelemetry.instrumentation.docs.utils.FileManager; import io.opentelemetry.instrumentation.docs.utils.YamlHelper; import java.io.BufferedWriter; import java.io.IOException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; @@ -26,20 +24,25 @@ public class DocGeneratorApplication { private static final Logger logger = Logger.getLogger(DocGeneratorApplication.class.getName()); - public static void main(String[] args) throws JsonProcessingException { - FileManager fileManager = new FileManager("instrumentation/"); + public static void main(String[] args) throws IOException { + // Identify path to repo so we can use absolute paths + String baseRepoPath = System.getProperty("basePath"); + if (baseRepoPath == null) { + baseRepoPath = "./"; + } else { + baseRepoPath += "/"; + } + + FileManager fileManager = new FileManager(baseRepoPath); List modules = new InstrumentationAnalyzer(fileManager).analyze(); try (BufferedWriter writer = - Files.newBufferedWriter( - Paths.get("docs/instrumentation-list.yaml"), Charset.defaultCharset())) { + Files.newBufferedWriter(Paths.get(baseRepoPath + "docs/instrumentation-list.yaml"))) { writer.write("# This file is generated and should not be manually edited.\n"); writer.write("# The structure and contents are a work in progress and subject to change.\n"); writer.write( "# For more information see: https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/13468\n\n"); YamlHelper.generateInstrumentationYaml(modules, writer); - } catch (IOException e) { - logger.severe("Error writing instrumentation list: " + e.getMessage()); } printStats(modules); diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java index 5489c61256eb..771b905d2330 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java @@ -7,15 +7,16 @@ import static io.opentelemetry.instrumentation.docs.parsers.GradleParser.parseGradleFile; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.exc.ValueInstantiationException; import io.opentelemetry.instrumentation.docs.internal.DependencyInfo; import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics; import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule; import io.opentelemetry.instrumentation.docs.internal.InstrumentationType; +import io.opentelemetry.instrumentation.docs.parsers.MetricParser; import io.opentelemetry.instrumentation.docs.utils.FileManager; import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath; import io.opentelemetry.instrumentation.docs.utils.YamlHelper; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -41,7 +42,7 @@ class InstrumentationAnalyzer { * @return a list of {@link InstrumentationModule} objects with aggregated types */ public static List convertToInstrumentationModules( - List paths) { + String rootPath, List paths) { Map moduleMap = new HashMap<>(); for (InstrumentationPath path : paths) { @@ -50,7 +51,7 @@ public static List convertToInstrumentationModules( moduleMap.put( key, new InstrumentationModule.Builder() - .srcPath(path.srcPath().replace("/javaagent", "").replace("/library", "")) + .srcPath(sanitizePathName(rootPath, path.srcPath())) .instrumentationName(path.instrumentationName()) .namespace(path.namespace()) .group(path.group()) @@ -61,16 +62,21 @@ public static List convertToInstrumentationModules( return new ArrayList<>(moduleMap.values()); } + private static String sanitizePathName(String rootPath, String path) { + return path.replace(rootPath, "").replace("/javaagent", "").replace("/library", ""); + } + /** * Traverses the given root directory to find all instrumentation paths and then analyzes them. - * Extracts version information from each instrumentation's build.gradle file, and other - * information from metadata.yaml files. + * Extracts version information from each instrumentation's build.gradle file, metric data from + * files in the .telemetry directories, and other information from metadata.yaml files. * * @return a list of {@link InstrumentationModule} */ - List analyze() throws JsonProcessingException { + List analyze() throws IOException { List paths = fileManager.getInstrumentationPaths(); - List modules = convertToInstrumentationModules(paths); + List modules = + convertToInstrumentationModules(fileManager.rootDir(), paths); for (InstrumentationModule module : modules) { List gradleFiles = fileManager.findBuildGradleFiles(module.getSrcPath()); @@ -86,12 +92,10 @@ List analyze() throws JsonProcessingException { } } - String emittedMetrics = fileManager.getMetrics(module.getSrcPath()); - if (emittedMetrics != null) { - EmittedMetrics metrics = YamlHelper.emittedMetricsParser(emittedMetrics); - if (metrics != null && metrics.getMetrics() != null) { - module.setMetrics(metrics.getMetrics()); - } + EmittedMetrics metrics = + MetricParser.getMetricsFromFiles(fileManager.rootDir(), module.getSrcPath()); + if (!metrics.getMetrics().isEmpty()) { + module.setMetrics(metrics.getMetrics()); } } return modules; @@ -100,7 +104,7 @@ List analyze() throws JsonProcessingException { void analyzeVersions(List files, InstrumentationModule module) { Map> versions = new HashMap<>(); for (String file : files) { - String fileContents = fileManager.readFileToString(file); + String fileContents = FileManager.readFileToString(file); if (fileContents == null) { continue; } diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/MetricParser.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/MetricParser.java new file mode 100644 index 000000000000..32fc6cf3e09c --- /dev/null +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/MetricParser.java @@ -0,0 +1,87 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.docs.parsers; + +import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics; +import io.opentelemetry.instrumentation.docs.utils.FileManager; +import io.opentelemetry.instrumentation.docs.utils.YamlHelper; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Stream; + +public class MetricParser { + private static final Logger logger = Logger.getLogger(MetricParser.class.getName()); + + /** + * Looks for metric files in the .telemetry directory, and combines them into a single list of + * metrics. + * + * @param instrumentationDirectory the directory to traverse + * @return contents of aggregated files + */ + public static EmittedMetrics getMetricsFromFiles( + String rootDir, String instrumentationDirectory) { + StringBuilder metricsContent = new StringBuilder("metrics:\n"); + Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry"); + + if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) { + try (Stream files = Files.list(telemetryDir)) { + files + .filter(path -> path.getFileName().toString().startsWith("metrics-")) + .forEach( + path -> { + String content = FileManager.readFileToString(path.toString()); + if (content != null) { + // Skip the first line of yaml ("metrics:") so we can aggregate into one list + int firstNewline = content.indexOf('\n'); + if (firstNewline != -1) { + String contentWithoutFirstLine = content.substring(firstNewline + 1); + metricsContent.append(contentWithoutFirstLine); + } + } + }); + } catch (IOException e) { + logger.severe("Error reading metrics files: " + e.getMessage()); + } + } + + return parseMetrics(metricsContent.toString()); + } + + /** + * Takes in a raw string representation of the aggregated EmittedMetrics yaml, deduplicates the + * metrics by name and then returns a new EmittedMetrics object. + * + * @param input raw string representation of EmittedMetrics yaml + * @return EmittedMetrics + */ + // visible for testing + public static EmittedMetrics parseMetrics(String input) { + EmittedMetrics metrics = YamlHelper.emittedMetricsParser(input); + if (metrics.getMetrics() == null) { + return new EmittedMetrics(Collections.emptyList()); + } + + // deduplicate metrics by name + Map deduplicatedMetrics = new HashMap<>(); + for (EmittedMetrics.Metric metric : metrics.getMetrics()) { + deduplicatedMetrics.put(metric.getName(), metric); + } + + List uniqueMetrics = new ArrayList<>(deduplicatedMetrics.values()); + return new EmittedMetrics(uniqueMetrics); + } + + private MetricParser() {} +} diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/FileManager.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/FileManager.java index 602c65abb129..f142cded4274 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/FileManager.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/FileManager.java @@ -17,25 +17,17 @@ import java.util.stream.Stream; import javax.annotation.Nullable; -public class FileManager { +public record FileManager(String rootDir) { private static final Logger logger = Logger.getLogger(FileManager.class.getName()); - private final String rootDir; - public FileManager(String rootDir) { - this.rootDir = rootDir; - } - - public List getInstrumentationPaths() { - Path rootPath = Paths.get(rootDir); + public List getInstrumentationPaths() throws IOException { + Path rootPath = Paths.get(rootDir + "instrumentation"); try (Stream walk = Files.walk(rootPath)) { return walk.filter(Files::isDirectory) .filter(dir -> isValidInstrumentationPath(dir.toString())) .map(dir -> parseInstrumentationPath(dir.toString())) .collect(Collectors.toList()); - } catch (IOException e) { - logger.severe("Error traversing directory: " + e.getMessage()); - return new ArrayList<>(); } } @@ -85,7 +77,7 @@ public static boolean isValidInstrumentationPath(String filePath) { } public List findBuildGradleFiles(String instrumentationDirectory) { - Path rootPath = Paths.get(instrumentationDirectory); + Path rootPath = Paths.get(rootDir + instrumentationDirectory); try (Stream walk = Files.walk(rootPath)) { return walk.filter(Files::isRegularFile) @@ -103,7 +95,7 @@ public List findBuildGradleFiles(String instrumentationDirectory) { @Nullable public String getMetaDataFile(String instrumentationDirectory) { - String metadataFile = instrumentationDirectory + "/metadata.yaml"; + String metadataFile = rootDir + instrumentationDirectory + "/metadata.yaml"; if (Files.exists(Paths.get(metadataFile))) { return readFileToString(metadataFile); } @@ -111,7 +103,7 @@ public String getMetaDataFile(String instrumentationDirectory) { } @Nullable - public String readFileToString(String filePath) { + public static String readFileToString(String filePath) { try { return Files.readString(Paths.get(filePath)); } catch (IOException e) { @@ -119,33 +111,4 @@ public String readFileToString(String filePath) { return null; } } - - /** - * Looks for metric files in the .telemetry directory - * - * @param instrumentationDirectory the directory to traverse - * @return contents of file - */ - public String getMetrics(String instrumentationDirectory) { - StringBuilder metricsContent = new StringBuilder(); - Path telemetryDir = Paths.get(instrumentationDirectory, ".telemetry"); - - if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) { - try (Stream files = Files.list(telemetryDir)) { - files - .filter(path -> path.getFileName().toString().startsWith("metrics-")) - .forEach( - path -> { - String content = readFileToString(path.toString()); - if (content != null) { - metricsContent.append(content).append("\n"); - } - }); - } catch (IOException e) { - logger.severe("Error reading metrics files: " + e.getMessage()); - } - } - - return metricsContent.toString(); - } } diff --git a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzerTest.java b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzerTest.java index 4d1f312255d8..ee39e6c1d84c 100644 --- a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzerTest.java +++ b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzerTest.java @@ -41,7 +41,7 @@ void testConvertToInstrumentationModule() { InstrumentationType.LIBRARY)); List modules = - InstrumentationAnalyzer.convertToInstrumentationModules(paths); + InstrumentationAnalyzer.convertToInstrumentationModules("test", paths); assertThat(modules.size()).isEqualTo(2); diff --git a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/parsers/MetricParserTest.java b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/parsers/MetricParserTest.java new file mode 100644 index 000000000000..531d40e720fc --- /dev/null +++ b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/parsers/MetricParserTest.java @@ -0,0 +1,88 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.docs.parsers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; + +import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics; +import io.opentelemetry.instrumentation.docs.utils.FileManager; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.MockedStatic; + +class MetricParserTest { + + @Test + void parseMetricsDeduplicatesMetricsByName() { + String input = + """ + metrics: + - name: metric1 + type: counter + - name: metric1 + type: counter + - name: metric2 + type: gauge + """; + + EmittedMetrics result = MetricParser.parseMetrics(input); + List metricNames = + result.getMetrics().stream().map(EmittedMetrics.Metric::getName).sorted().toList(); + + assertThat(metricNames).hasSize(2); + assertThat(metricNames).containsExactly("metric1", "metric2"); + } + + @Test + void parseMetricsHandlesEmptyInput() { + String input = "metrics:\n"; + EmittedMetrics result = MetricParser.parseMetrics(input); + assertThat(result.getMetrics()).isEmpty(); + } + + @Test + void getMetricsFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException { + Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry")); + + String file1Content = "metrics:\n - name: metric1\n type: counter\n"; + String file2Content = "metrics:\n - name: metric2\n type: gauge\n"; + + Files.writeString(telemetryDir.resolve("metrics-1.yaml"), file1Content); + Files.writeString(telemetryDir.resolve("metrics-2.yaml"), file2Content); + + // Create a non-metrics file that should be ignored + Files.writeString(telemetryDir.resolve("other-file.yaml"), "some content"); + + try (MockedStatic fileManagerMock = mockStatic(FileManager.class)) { + fileManagerMock + .when( + () -> FileManager.readFileToString(telemetryDir.resolve("metrics-1.yaml").toString())) + .thenReturn(file1Content); + fileManagerMock + .when( + () -> FileManager.readFileToString(telemetryDir.resolve("metrics-2.yaml").toString())) + .thenReturn(file2Content); + + EmittedMetrics result = MetricParser.getMetricsFromFiles(tempDir.toString(), ""); + + assertThat(result.getMetrics()).hasSize(2); + List metricNames = + result.getMetrics().stream().map(EmittedMetrics.Metric::getName).sorted().toList(); + assertThat(metricNames).containsExactly("metric1", "metric2"); + } + } + + @Test + void getMetricsFromFilesHandlesNonexistentDirectory() { + EmittedMetrics result = MetricParser.getMetricsFromFiles("/nonexistent", "path"); + assertThat(result.getMetrics()).isEmpty(); + } +} diff --git a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/FileManagerTest.java b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/FileManagerTest.java index 9e248a0c3eb7..feeacee17fda 100644 --- a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/FileManagerTest.java +++ b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/FileManagerTest.java @@ -24,7 +24,7 @@ class FileManagerTest { @BeforeEach void setUp() { - fileManager = new FileManager(tempDir.toString()); + fileManager = new FileManager(tempDir.toString() + "/"); } @Test @@ -52,13 +52,4 @@ void testExcludesCommonModules() { "instrumentation/elasticsearch/elasticsearch-rest-common-5.0")) .isFalse(); } - - @Test - void testFindBuildGradleFiles() throws IOException { - Path gradleFile = Files.createFile(tempDir.resolve("build.gradle.kts")); - Path nonGradleFile = Files.createFile(tempDir.resolve("gradle.properties")); - List gradleFiles = fileManager.findBuildGradleFiles(tempDir.toString()); - assertThat(gradleFiles).contains(gradleFile.toString()); - assertThat(gradleFiles).doesNotContain(nonGradleFile.toString()); - } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts index d461f4ed9299..9df1916a021a 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts @@ -132,24 +132,16 @@ testing { suites { val s3PresignerTest by registering(JvmTestSuite::class) { dependencies { - if (latestDepTest) { - implementation("software.amazon.awssdk:s3:latest.release") - } else { - implementation("software.amazon.awssdk:s3:2.10.12") - } + val version = if (latestDepTest) "latest.release" else "2.10.12" + implementation("software.amazon.awssdk:s3:$version") implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:library")) } } val s3CrtTest by registering(JvmTestSuite::class) { dependencies { - if (latestDepTest) { - implementation("software.amazon.awssdk:s3:latest.release") - implementation("software.amazon.awssdk.crt:aws-crt:latest.release") - } else { - implementation("software.amazon.awssdk:s3:2.27.21") - implementation("software.amazon.awssdk.crt:aws-crt:0.30.11") - } + implementation("software.amazon.awssdk:s3:" + if (latestDepTest) "latest.release" else "2.27.21") + implementation("software.amazon.awssdk.crt:aws-crt:" + if (latestDepTest) "latest.release" else "0.30.11") implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:library")) implementation("org.testcontainers:localstack") } @@ -158,12 +150,9 @@ testing { val testBedrockRuntime by registering(JvmTestSuite::class) { dependencies { implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing")) - if (findProperty("testLatestDeps") as Boolean) { - implementation("software.amazon.awssdk:bedrockruntime:latest.release") - } else { - // First release with Converse API - implementation("software.amazon.awssdk:bedrockruntime:2.25.63") - } + // 2.25.63 is the first release with Converse API + val version = if (latestDepTest) "latest.release" else "2.25.63" + implementation("software.amazon.awssdk:bedrockruntime:$version") } targets { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts index 45adb459fe05..cc196ed11e75 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts @@ -29,6 +29,8 @@ dependencies { testLibrary("software.amazon.awssdk:ses:2.2.0") } +val testLatestDeps = findProperty("testLatestDeps") as Boolean + testing { suites { val testCoreOnly by registering(JvmTestSuite::class) { @@ -36,17 +38,11 @@ testing { implementation(project()) implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing")) compileOnly("software.amazon.awssdk:sqs:2.2.0") - if (findProperty("testLatestDeps") as Boolean) { - implementation("software.amazon.awssdk:aws-core:latest.release") - implementation("software.amazon.awssdk:aws-json-protocol:latest.release") - implementation("software.amazon.awssdk:dynamodb:latest.release") - implementation("software.amazon.awssdk:lambda:latest.release") - } else { - implementation("software.amazon.awssdk:aws-core:2.2.0") - implementation("software.amazon.awssdk:aws-json-protocol:2.2.0") - implementation("software.amazon.awssdk:dynamodb:2.2.0") - implementation("software.amazon.awssdk:lambda:2.2.0") - } + val version = if (testLatestDeps) "latest.release" else "2.2.0" + implementation("software.amazon.awssdk:aws-core:$version") + implementation("software.amazon.awssdk:aws-json-protocol:$version") + implementation("software.amazon.awssdk:dynamodb:$version") + implementation("software.amazon.awssdk:lambda:$version") } } @@ -54,11 +50,8 @@ testing { dependencies { implementation(project()) implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing")) - if (findProperty("testLatestDeps") as Boolean) { - implementation("software.amazon.awssdk:lambda:latest.release") - } else { - implementation("software.amazon.awssdk:lambda:2.17.0") - } + val version = if (testLatestDeps) "latest.release" else "2.17.0" + implementation("software.amazon.awssdk:lambda:$version") } } @@ -66,11 +59,8 @@ testing { dependencies { implementation(project()) implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing")) - if (findProperty("testLatestDeps") as Boolean) { - implementation("software.amazon.awssdk:bedrockruntime:latest.release") - } else { - implementation("software.amazon.awssdk:bedrockruntime:2.25.63") - } + val version = if (testLatestDeps) "latest.release" else "2.25.63" + implementation("software.amazon.awssdk:bedrockruntime:$version") } } } diff --git a/instrumentation/azure-core/azure-core-1.19/javaagent/build.gradle.kts b/instrumentation/azure-core/azure-core-1.19/javaagent/build.gradle.kts index 0a32d0161335..d2c04ca8e627 100644 --- a/instrumentation/azure-core/azure-core-1.19/javaagent/build.gradle.kts +++ b/instrumentation/azure-core/azure-core-1.19/javaagent/build.gradle.kts @@ -39,11 +39,8 @@ testing { // extracted to the output directory are not available during tests val testAzure by registering(JvmTestSuite::class) { dependencies { - if (latestDepTest) { - implementation("com.azure:azure-core:1.35.0") - } else { - implementation("com.azure:azure-core:1.19.0") - } + val version = if (latestDepTest) "1.35.0" else "1.19.0" + implementation("com.azure:azure-core:$version") } } } diff --git a/instrumentation/jsf/jsf-mojarra-1.2/javaagent/build.gradle.kts b/instrumentation/jsf/jsf-mojarra-1.2/javaagent/build.gradle.kts index d20834a5162f..7e69f39c3cab 100644 --- a/instrumentation/jsf/jsf-mojarra-1.2/javaagent/build.gradle.kts +++ b/instrumentation/jsf/jsf-mojarra-1.2/javaagent/build.gradle.kts @@ -63,11 +63,8 @@ testing { implementation("javax.faces:jsf-api:1.2") implementation("com.sun.facelets:jsf-facelets:1.1.14") - if (latestDepTest) { - implementation("javax.faces:jsf-impl:1.+") - } else { - implementation("javax.faces:jsf-impl:1.2_04") - } + val version = if (latestDepTest) "1.+" else "1.2_04" + implementation("javax.faces:jsf-impl:$version") } } @@ -75,11 +72,8 @@ testing { dependencies { implementation(project(":instrumentation:jsf:jsf-javax-common:testing")) - if (latestDepTest) { - implementation("org.glassfish:jakarta.faces:2.+") - } else { - implementation("org.glassfish:javax.faces:2.2.0") - } + val version = if (latestDepTest) "2.+" else "2.2.0" + implementation("org.glassfish:javax.faces:$version") } } } diff --git a/instrumentation/jsf/jsf-myfaces-1.2/javaagent/build.gradle.kts b/instrumentation/jsf/jsf-myfaces-1.2/javaagent/build.gradle.kts index 355f24b6e743..559867c4de1f 100644 --- a/instrumentation/jsf/jsf-myfaces-1.2/javaagent/build.gradle.kts +++ b/instrumentation/jsf/jsf-myfaces-1.2/javaagent/build.gradle.kts @@ -31,11 +31,8 @@ testing { implementation(project(":instrumentation:jsf:jsf-javax-common:testing")) implementation("com.sun.facelets:jsf-facelets:1.1.14") - if (latestDepTest) { - implementation("org.apache.myfaces.core:myfaces-impl:1.2.+") - } else { - implementation("org.apache.myfaces.core:myfaces-impl:1.2.2") - } + val version = if (latestDepTest) "1.2.+" else "1.2.2" + implementation("org.apache.myfaces.core:myfaces-impl:$version") } } @@ -45,11 +42,8 @@ testing { implementation("javax.xml.bind:jaxb-api:2.2.11") implementation("com.sun.xml.bind:jaxb-impl:2.2.11") - if (latestDepTest) { - implementation("org.apache.myfaces.core:myfaces-impl:2.+") - } else { - implementation("org.apache.myfaces.core:myfaces-impl:2.2.0") - } + val version = if (latestDepTest) "2.+" else "2.2.0" + implementation("org.apache.myfaces.core:myfaces-impl:$version") } } } diff --git a/instrumentation/kotlinx-coroutines/kotlinx-coroutines-1.0/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt b/instrumentation/kotlinx-coroutines/kotlinx-coroutines-1.0/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt index f95bba068488..0509c243ca11 100644 --- a/instrumentation/kotlinx-coroutines/kotlinx-coroutines-1.0/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt +++ b/instrumentation/kotlinx-coroutines/kotlinx-coroutines-1.0/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt @@ -46,13 +46,11 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import org.junit.jupiter.params.provider.ArgumentsSource +import org.junit.jupiter.params.provider.MethodSource import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.function.Consumer @@ -83,7 +81,7 @@ class KotlinCoroutinesInstrumentationTest { val tracer = testing.openTelemetry.getTracer("test") @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `cancellation prevents trace`(dispatcher: DispatcherWrapper) { runCatching { runTest(dispatcher) { @@ -116,7 +114,7 @@ class KotlinCoroutinesInstrumentationTest { } @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `propagates across nested jobs`(dispatcher: DispatcherWrapper) { runTest(dispatcher) { val goodDeferred = async { 1 } @@ -310,7 +308,7 @@ class KotlinCoroutinesInstrumentationTest { } @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `traced mono`(dispatcherWrapper: DispatcherWrapper) { runTest(dispatcherWrapper) { mono(dispatcherWrapper.dispatcher) { @@ -337,7 +335,7 @@ class KotlinCoroutinesInstrumentationTest { private val animalKey: ContextKey = ContextKey.named("animal") @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `context contains expected value`(dispatcher: DispatcherWrapper) { runTest(dispatcher) { val context1 = Context.current().with(animalKey, "cat") @@ -512,18 +510,16 @@ class KotlinCoroutinesInstrumentationTest { span.end() } - class DispatchersSource : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream = Stream.of( - // Wrap dispatchers since it seems that ParameterizedTest tries to automatically close - // Closeable arguments with no way to avoid it. - arguments(DispatcherWrapper(Dispatchers.Default)), - arguments(DispatcherWrapper(Dispatchers.IO)), - arguments(DispatcherWrapper(Dispatchers.Unconfined)), - arguments(DispatcherWrapper(threadPool.asCoroutineDispatcher())), - arguments(DispatcherWrapper(singleThread.asCoroutineDispatcher())), - arguments(DispatcherWrapper(vertx.dispatcher())) - ) - } + private fun dispatchersSourceArguments(): Stream = Stream.of( + // Wrap dispatchers since it seems that ParameterizedTest tries to automatically close + // Closeable arguments with no way to avoid it. + arguments(DispatcherWrapper(Dispatchers.Default)), + arguments(DispatcherWrapper(Dispatchers.IO)), + arguments(DispatcherWrapper(Dispatchers.Unconfined)), + arguments(DispatcherWrapper(threadPool.asCoroutineDispatcher())), + arguments(DispatcherWrapper(singleThread.asCoroutineDispatcher())), + arguments(DispatcherWrapper(vertx.dispatcher())) + ) class DispatcherWrapper(val dispatcher: CoroutineDispatcher) { override fun toString(): String = dispatcher.toString() @@ -566,7 +562,7 @@ class KotlinCoroutinesInstrumentationTest { // regression test for // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/11411 @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `dispatch does not propagate context`(dispatcher: DispatcherWrapper) { Assumptions.assumeTrue(dispatcher.dispatcher != Dispatchers.Unconfined) diff --git a/instrumentation/kotlinx-coroutines/kotlinx-coroutines-flow-1.3/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutines13InstrumentationTest.kt b/instrumentation/kotlinx-coroutines/kotlinx-coroutines-flow-1.3/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutines13InstrumentationTest.kt index 62902124a599..11acbd446894 100644 --- a/instrumentation/kotlinx-coroutines/kotlinx-coroutines-flow-1.3/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutines13InstrumentationTest.kt +++ b/instrumentation/kotlinx-coroutines/kotlinx-coroutines-flow-1.3/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutines13InstrumentationTest.kt @@ -27,13 +27,11 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import org.junit.jupiter.params.provider.ArgumentsSource +import org.junit.jupiter.params.provider.MethodSource import java.util.concurrent.Executors import java.util.stream.Stream @@ -58,7 +56,7 @@ class KotlinCoroutines13InstrumentationTest { val tracer = testing.openTelemetry.getTracer("test") @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `traced across channels`(dispatcher: DispatcherWrapper) { runTest(dispatcher) { val producer = produce { @@ -110,7 +108,7 @@ class KotlinCoroutines13InstrumentationTest { } @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `traced mono with context propagation operator`(dispatcherWrapper: DispatcherWrapper) { runTest(dispatcherWrapper) { val currentContext = Context.current() @@ -145,7 +143,7 @@ class KotlinCoroutines13InstrumentationTest { } @ParameterizedTest - @ArgumentsSource(DispatchersSource::class) + @MethodSource("dispatchersSourceArguments") fun `traced flux`(dispatcherWrapper: DispatcherWrapper) { runTest(dispatcherWrapper) { flux(dispatcherWrapper.dispatcher) { @@ -198,17 +196,15 @@ class KotlinCoroutines13InstrumentationTest { } } - class DispatchersSource : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream = Stream.of( - // Wrap dispatchers since it seems that ParameterizedTest tries to automatically close - // Closeable arguments with no way to avoid it. - arguments(DispatcherWrapper(Dispatchers.Default)), - arguments(DispatcherWrapper(Dispatchers.IO)), - arguments(DispatcherWrapper(Dispatchers.Unconfined)), - arguments(DispatcherWrapper(threadPool.asCoroutineDispatcher())), - arguments(DispatcherWrapper(singleThread.asCoroutineDispatcher())), - ) - } + private fun dispatchersSourceArguments(): Stream = Stream.of( + // Wrap dispatchers since it seems that ParameterizedTest tries to automatically close + // Closeable arguments with no way to avoid it. + arguments(DispatcherWrapper(Dispatchers.Default)), + arguments(DispatcherWrapper(Dispatchers.IO)), + arguments(DispatcherWrapper(Dispatchers.Unconfined)), + arguments(DispatcherWrapper(threadPool.asCoroutineDispatcher())), + arguments(DispatcherWrapper(singleThread.asCoroutineDispatcher())), + ) class DispatcherWrapper(val dispatcher: CoroutineDispatcher) { override fun toString(): String = dispatcher.toString() diff --git a/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts b/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts index edf118bbb314..52ab7aa8b6df 100644 --- a/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts +++ b/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts @@ -47,6 +47,26 @@ graalvmNative { binaries.all { resources.autodetect() + + // see https://github.com/junit-team/junit5/wiki/Upgrading-to-JUnit-5.13 + // should not be needed after updating native build tools to 0.11.0 + val initializeAtBuildTime = listOf( + "org.junit.jupiter.api.DisplayNameGenerator\$IndicativeSentences", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$ClassInfo", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$LifecycleMethods", + "org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor", + "org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor", + "org.junit.jupiter.engine.descriptor.DynamicDescendantFilter\$Mode", + "org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector\$1", + "org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor\$MethodInfo", + "org.junit.jupiter.engine.discovery.ClassSelectorResolver\$DummyClassTemplateInvocationContext", + "org.junit.platform.engine.support.store.NamespacedHierarchicalStore\$EvaluatedValue", + "org.junit.platform.launcher.core.DiscoveryIssueNotifier", + "org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider", + "org.junit.platform.launcher.core.LauncherDiscoveryResult\$EngineResultInfo", + "org.junit.platform.suite.engine.SuiteTestDescriptor\$LifecycleMethods", + ) + buildArgs.add("--initialize-at-build-time=${initializeAtBuildTime.joinToString(",")}") } // See https://github.com/graalvm/native-build-tools/issues/572 diff --git a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts index 8812cd8886ea..c0f6663a272b 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts @@ -41,17 +41,16 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-extension-annotations") } +val testLatestDeps = findProperty("testLatestDeps") as Boolean + testing { suites { val testInitialization by registering(JvmTestSuite::class) { dependencies { implementation(project(":instrumentation:reactor:reactor-3.1:library")) implementation(project(":instrumentation-annotations")) - if (findProperty("testLatestDeps") as Boolean) { - implementation("io.projectreactor:reactor-test:latest.release") - } else { - implementation("io.projectreactor:reactor-test:3.1.0.RELEASE") - } + val version = if (testLatestDeps) "latest.release" else "3.1.0.RELEASE" + implementation("io.projectreactor:reactor-test:$version") } } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index bb1c806a3ee3..6b2a99d0c521 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -7,6 +7,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelMapConverter; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.SdkEnabled; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties; @@ -28,6 +29,8 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -54,6 +57,8 @@ OtelSpringProperties.class }) public class OpenTelemetryAutoConfiguration { + private static final Logger logger = + LoggerFactory.getLogger(OpenTelemetryAutoConfiguration.class); public OpenTelemetryAutoConfiguration() {} @@ -110,6 +115,11 @@ public AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk( @Bean public OpenTelemetry openTelemetry( AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + logger.info( + "OpenTelemetry Spring Boot starter ({}) has been started", + EmbeddedInstrumentationProperties.findVersion( + "io.opentelemetry.spring-boot-autoconfigure")); + return autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); } @@ -146,6 +156,8 @@ public OtelMapConverter otelMapConverter() { @Bean public OpenTelemetry openTelemetry() { + logger.info("OpenTelemetry Spring Boot starter has been disabled"); + return OpenTelemetry.noop(); } diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts b/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts index a6ea07b9849b..9e4134b2a079 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts @@ -40,15 +40,11 @@ testing { implementation(project(":instrumentation:spring:spring-kafka-2.7:testing")) // the "library" configuration is not recognized by the test suite plugin - if (latestDepTest) { - implementation("org.springframework.kafka:spring-kafka:latest.release") - implementation("org.springframework.boot:spring-boot-starter-test:latest.release") - implementation("org.springframework.boot:spring-boot-starter:latest.release") - } else { - implementation("org.springframework.kafka:spring-kafka:2.7.0") - implementation("org.springframework.boot:spring-boot-starter-test:2.5.3") - implementation("org.springframework.boot:spring-boot-starter:2.5.3") - } + val springKafkaVersion = if (latestDepTest) "latest.release" else "2.7.0" + val springBootVersion = if (latestDepTest) "latest.release" else "2.5.3" + implementation("org.springframework.kafka:spring-kafka:$springKafkaVersion") + implementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") + implementation("org.springframework.boot:spring-boot-starter:$springBootVersion") } targets { diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts index cd1cdaaa78d8..49dc47e87439 100644 --- a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts @@ -22,9 +22,13 @@ dependencies { testInstrumentation(project(":instrumentation:rxjava:rxjava-2.0:javaagent")) testInstrumentation(project(":instrumentation:vertx:vertx-http-client:vertx-http-client-3.0:javaagent")) testInstrumentation(project(":instrumentation:vertx:vertx-http-client:vertx-http-client-4.0:javaagent")) + testInstrumentation(project(":instrumentation:vertx:vertx-http-client:vertx-http-client-5.0:javaagent")) + testInstrumentation(project(":instrumentation:vertx:vertx-sql-client:vertx-sql-client-5.0:javaagent")) testInstrumentation(project(":instrumentation:vertx:vertx-web-3.0:javaagent")) } +val testLatestDeps = findProperty("testLatestDeps") as Boolean + testing { suites { val version35Test by registering(JvmTestSuite::class) { @@ -33,50 +37,66 @@ testing { // inclusion of this artifact inside :testing-common compileOnly(project.dependencies.project(":testing:armeria-shaded-for-testing", configuration = "shadow")) + val version = if (testLatestDeps) "3.+" else "3.5.0" + implementation("org.hsqldb:hsqldb:2.3.4") + compileOnly("io.vertx:vertx-codegen:$version") + implementation("io.vertx:vertx-web:$version") + implementation("io.vertx:vertx-rx-java2:$version") + implementation("io.vertx:vertx-web-client:$version") + implementation("io.vertx:vertx-jdbc-client:$version") + implementation("io.vertx:vertx-circuit-breaker:$version") + } + } + + val version41Test by registering(JvmTestSuite::class) { + dependencies { + // this only exists to make Intellij happy since it doesn't (currently at least) understand our + // inclusion of this artifact inside :testing-common + compileOnly(project.dependencies.project(":testing:armeria-shaded-for-testing", configuration = "shadow")) + + val version = if (testLatestDeps) "4.+" else "4.1.0" implementation("org.hsqldb:hsqldb:2.3.4") - compileOnly("io.vertx:vertx-codegen:$vertxVersion") - implementation("io.vertx:vertx-web:$vertxVersion") - implementation("io.vertx:vertx-rx-java2:$vertxVersion") - implementation("io.vertx:vertx-web-client:$vertxVersion") - implementation("io.vertx:vertx-jdbc-client:$vertxVersion") - implementation("io.vertx:vertx-circuit-breaker:$vertxVersion") + compileOnly("io.vertx:vertx-codegen:$version") + implementation("io.vertx:vertx-web:$version") + implementation("io.vertx:vertx-rx-java2:$version") + implementation("io.vertx:vertx-web-client:$version") + implementation("io.vertx:vertx-jdbc-client:$version") + implementation("io.vertx:vertx-circuit-breaker:$version") } } - val latestDepTest by registering(JvmTestSuite::class) { + val version5Test by registering(JvmTestSuite::class) { dependencies { // this only exists to make Intellij happy since it doesn't (currently at least) understand our // inclusion of this artifact inside :testing-common compileOnly(project.dependencies.project(":testing:armeria-shaded-for-testing", configuration = "shadow")) + val version = if (testLatestDeps) "latest.release" else "5.0.0" implementation("org.hsqldb:hsqldb:2.3.4") - implementation("io.vertx:vertx-web:4.+") - implementation("io.vertx:vertx-rx-java2:4.+") - implementation("io.vertx:vertx-web-client:4.+") - implementation("io.vertx:vertx-jdbc-client:4.+") - implementation("io.vertx:vertx-circuit-breaker:4.+") + compileOnly("io.vertx:vertx-codegen:$version") + implementation("io.vertx:vertx-web:$version") + implementation("io.vertx:vertx-rx-java2:$version") + implementation("io.vertx:vertx-web-client:$version") + implementation("io.vertx:vertx-jdbc-client:$version") + implementation("io.vertx:vertx-circuit-breaker:$version") } } } } -val testLatestDeps = findProperty("testLatestDeps") as Boolean - tasks { - if (testLatestDeps) { - // disable regular test running and compiling tasks when latest dep test task is run - named("test") { - enabled = false - } - named("compileTestGroovy") { + named("compileVersion5TestJava", JavaCompile::class).configure { + options.release.set(11) + } + val testJavaVersion = + gradle.startParameter.projectProperties.get("testJavaVersion")?.let(JavaVersion::toVersion) + ?: JavaVersion.current() + if (!testJavaVersion.isCompatibleWith(JavaVersion.VERSION_11)) { + named("version5Test", Test::class).configure { enabled = false } } - named("latestDepTest") { - enabled = testLatestDeps - } - check { dependsOn(testing.suites) } diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/netty/handler/codec/haproxy/HAProxyMessage.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/netty/handler/codec/haproxy/HAProxyMessage.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/netty/handler/codec/haproxy/HAProxyMessage.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java similarity index 100% rename from instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java rename to instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version41Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/netty/handler/codec/haproxy/HAProxyMessage.java new file mode 100644 index 000000000000..3dcf2cafedfe --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/netty/handler/codec/haproxy/HAProxyMessage.java @@ -0,0 +1,10 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.netty.handler.codec.haproxy; + +// instrumentation fails without this class +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class HAProxyMessage {} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java new file mode 100644 index 000000000000..5659024f96a0 --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java @@ -0,0 +1,10 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.netty.handler.codec.haproxy; + +// instrumentation fails without this class +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class HAProxyProxiedProtocol {} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java new file mode 100644 index 000000000000..deaddbe083a0 --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerSingleConnection.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.client; + +import io.vertx.core.AsyncResult; +import io.vertx.reactivex.circuitbreaker.CircuitBreaker; +import io.vertx.reactivex.ext.web.client.HttpRequest; +import io.vertx.reactivex.ext.web.client.HttpResponse; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +public class VertxRxCircuitBreakerSingleConnection extends VertxRxSingleConnection { + private final CircuitBreaker breaker; + + public VertxRxCircuitBreakerSingleConnection(String host, int port, CircuitBreaker breaker) { + super(host, port); + this.breaker = breaker; + } + + @Override + protected HttpResponse fetchResponse(HttpRequest request) { + CompletableFuture future = new CompletableFuture<>(); + + sendRequestWithCallback( + request, + it -> { + if (it.succeeded()) { + future.complete(it.result()); + } else { + future.completeExceptionally(it.cause()); + } + }); + + return (HttpResponse) future.join(); + } + + private void sendRequestWithCallback(HttpRequest request, Consumer> consumer) { + breaker + .execute( + command -> + request + .rxSend() + .doOnSuccess(command::complete) + .doOnError(command::fail) + .subscribe()) + .onComplete(consumer::accept); + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java new file mode 100644 index 000000000000..df2b057e87ea --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxCircuitBreakerWebClientTest.java @@ -0,0 +1,143 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.client; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.VertxOptions; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.reactivex.circuitbreaker.CircuitBreaker; +import io.vertx.reactivex.core.Promise; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.ext.web.client.HttpRequest; +import io.vertx.reactivex.ext.web.client.HttpResponse; +import io.vertx.reactivex.ext.web.client.WebClient; +import java.net.URI; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import org.junit.jupiter.api.extension.RegisterExtension; + +class VertxRxCircuitBreakerWebClientTest extends AbstractHttpClientTest> { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + private final Vertx vertx = Vertx.vertx(new VertxOptions()); + private final WebClient httpClient = buildClient(vertx); + private final CircuitBreaker breaker = + CircuitBreaker.create( + "my-circuit-breaker", + vertx, + new CircuitBreakerOptions() + .setTimeout(-1) // Disable the timeout otherwise it makes each test take this long. + ); + + private static WebClient buildClient(Vertx vertx) { + WebClientOptions clientOptions = + new WebClientOptions().setConnectTimeout(Math.toIntExact(CONNECTION_TIMEOUT.toMillis())); + return WebClient.create(vertx, clientOptions); + } + + @Override + public HttpRequest buildRequest(String method, URI uri, Map headers) { + HttpRequest request = httpClient.requestAbs(HttpMethod.valueOf(method), uri.toString()); + headers.forEach(request::putHeader); + return request; + } + + @Override + public int sendRequest( + HttpRequest request, String method, URI uri, Map headers) + throws ExecutionException, InterruptedException { + // VertxRx doesn't seem to provide a synchronous API at all for circuit breaker. Bridge through + // a callback. + CompletableFuture future = new CompletableFuture<>(); + sendRequestWithCallback( + request, + result -> { + if (result.succeeded()) { + future.complete(result.result().statusCode()); + } else { + future.completeExceptionally(result.cause()); + } + }); + + return future.get(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void sendRequestWithCallback( + HttpRequest request, Consumer>> consumer) { + breaker + .execute( + (Handler>>) + command -> request.rxSend().subscribe(command::complete, command::fail)) + .onComplete(consumer::accept); + } + + @Override + public void sendRequestWithCallback( + HttpRequest request, + String method, + URI uri, + Map headers, + HttpClientResult requestResult) { + sendRequestWithCallback( + request, + result -> { + if (result.succeeded()) { + requestResult.complete(result.result().statusCode()); + } else { + requestResult.complete(result.cause()); + } + }); + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + optionsBuilder.disableTestRedirects(); + optionsBuilder.disableTestHttps(); + optionsBuilder.disableTestReadTimeout(); + optionsBuilder.setHttpAttributes(VertxRxCircuitBreakerWebClientTest::getHttpAttributes); + optionsBuilder.setExpectedClientSpanNameMapper( + VertxRxCircuitBreakerWebClientTest::expectedClientSpanName); + optionsBuilder.setSingleConnectionFactory( + (host, port) -> new VertxRxCircuitBreakerSingleConnection(host, port, breaker)); + } + + private static Set> getHttpAttributes(URI uri) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "http://192.0.2.1/": // non routable address + return Collections.emptySet(); + default: + return HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES; + } + } + + private static String expectedClientSpanName(URI uri, String method) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "http://192.0.2.1/": // non routable address + return "CONNECT"; + default: + return HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER.apply(uri, method); + } + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java new file mode 100644 index 000000000000..cda07b5c89cc --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxSingleConnection.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.client; + +import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection; +import io.vertx.core.VertxOptions; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.PoolOptions; +import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.ext.web.client.HttpRequest; +import io.vertx.reactivex.ext.web.client.HttpResponse; +import io.vertx.reactivex.ext.web.client.WebClient; +import java.util.Map; +import java.util.Objects; + +public class VertxRxSingleConnection implements SingleConnection { + private final WebClient webClient; + private final String host; + private final int port; + + public VertxRxSingleConnection(String host, int port) { + this.host = host; + this.port = port; + + WebClientOptions clientOptions = + new WebClientOptions().setConnectTimeout(5000).setKeepAlive(true).setPipelining(true); + PoolOptions poolOptions = new PoolOptions().setHttp1MaxSize(1); + + Vertx vertx = Vertx.vertx(new VertxOptions()); + this.webClient = WebClient.create(vertx, clientOptions, poolOptions); + } + + @Override + public int doRequest(String path, Map headers) { + String requestId = Objects.requireNonNull(headers.get(REQUEST_ID_HEADER)); + + HttpRequest request = webClient.request(HttpMethod.GET, port, host, path); + headers.forEach(request::putHeader); + + HttpResponse response = fetchResponse(request); + + String responseId = response.getHeader(REQUEST_ID_HEADER); + if (!requestId.equals(responseId)) { + throw new IllegalStateException( + String.format("Received response with id %s, expected %s", responseId, requestId)); + } + + return response.statusCode(); + } + + protected HttpResponse fetchResponse(HttpRequest request) { + return request.rxSend().blockingGet(); + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java new file mode 100644 index 000000000000..afb69b9f0db3 --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/client/VertxRxWebClientTest.java @@ -0,0 +1,116 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.client; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.reactivex.functions.Consumer; +import io.vertx.core.VertxOptions; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.ext.web.client.HttpRequest; +import io.vertx.reactivex.ext.web.client.HttpResponse; +import io.vertx.reactivex.ext.web.client.WebClient; +import java.net.URI; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.extension.RegisterExtension; + +class VertxRxWebClientTest extends AbstractHttpClientTest> { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + private final WebClient httpClient = buildClient(); + + private static WebClient buildClient() { + Vertx vertx = Vertx.vertx(new VertxOptions()); + WebClientOptions clientOptions = + new WebClientOptions().setConnectTimeout(Math.toIntExact(CONNECTION_TIMEOUT.toMillis())); + return WebClient.create(vertx, clientOptions); + } + + @Override + public HttpRequest buildRequest(String method, URI uri, Map headers) { + HttpRequest request = httpClient.requestAbs(HttpMethod.valueOf(method), uri.toString()); + headers.forEach(request::putHeader); + return request; + } + + @Override + public int sendRequest( + HttpRequest request, String method, URI uri, Map headers) { + return request.rxSend().blockingGet().statusCode(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void sendRequestWithCallback( + HttpRequest request, + String method, + URI uri, + Map headers, + HttpClientResult requestResult) { + request + .rxSend() + .subscribe( + (Consumer>) + httpResponse -> requestResult.complete(httpResponse.statusCode()), + requestResult::complete); + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + optionsBuilder.disableTestRedirects(); + optionsBuilder.disableTestHttps(); + optionsBuilder.disableTestReadTimeout(); + optionsBuilder.setHttpAttributes(VertxRxWebClientTest::getHttpAttributes); + optionsBuilder.setClientSpanErrorMapper(VertxRxWebClientTest::clientSpanError); + optionsBuilder.setExpectedClientSpanNameMapper(VertxRxWebClientTest::expectedClientSpanName); + optionsBuilder.setSingleConnectionFactory(VertxRxSingleConnection::new); + } + + private static Set> getHttpAttributes(URI uri) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "http://192.0.2.1/": // non routable address + return Collections.emptySet(); + default: + return HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES; + } + } + + private static Throwable clientSpanError(URI uri, Throwable exception) { + if (exception.getClass() == RuntimeException.class) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "http://192.0.2.1/": // non routable address + exception = exception.getCause(); + break; + default: + break; + } + } + return exception; + } + + private static String expectedClientSpanName(URI uri, String method) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "http://192.0.2.1/": // non routable address + return "CONNECT"; + default: + return HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER.apply(uri, method); + } + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java new file mode 100644 index 000000000000..fcd029eb195e --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxHttpServerTest.java @@ -0,0 +1,110 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.server; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; + +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.json.JsonObject; +import io.vertx.reactivex.core.AbstractVerticle; +import io.vertx.reactivex.ext.web.Router; +import io.vertx.reactivex.ext.web.RoutingContext; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +abstract class AbstractVertxRxHttpServerTest extends AbstractHttpServerTest { + static final String CONFIG_HTTP_SERVER_PORT = "http.server.port"; + + @Override + protected Vertx setupServer() throws Exception { + Vertx server = + Vertx.vertx( + new VertxOptions() + // Useful for debugging: + // .setBlockedThreadCheckInterval(Integer.MAX_VALUE) + ); + CompletableFuture future = new CompletableFuture<>(); + server + .deployVerticle( + verticle().getName(), + new DeploymentOptions() + .setConfig(new JsonObject().put(CONFIG_HTTP_SERVER_PORT, port)) + .setInstances(3)) + .onComplete( + result -> { + if (!result.succeeded()) { + throw new IllegalStateException("Cannot deploy server Verticle", result.cause()); + } + future.complete(null); + }); + + future.get(30, TimeUnit.SECONDS); + return server; + } + + @Override + protected void stopServer(Vertx vertx) { + vertx.close(); + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + + options.setTestPathParam(true); + // server spans are ended inside the controller spans + options.setVerifyServerSpanEndTime(false); + options.setExpectedHttpRoute( + (endpoint, method) -> { + if (HttpConstants._OTHER.equals(method)) { + return getContextPath() + endpoint.getPath(); + } + return expectedHttpRoute(endpoint, method); + }); + } + + protected Class verticle() { + return VertxReactiveWebServer.class; + } + + public static class VertxReactiveWebServer extends AbstractVertxRxVerticle { + @Override + void handle(RoutingContext ctx, ServerEndpoint endpoint, Runnable action) { + controller(endpoint, action::run); + } + + @Override + public void start(Promise startFuture) { + int port = config().getInteger(CONFIG_HTTP_SERVER_PORT); + Router router = Router.router(vertx); + + configure(router); + router + .route(EXCEPTION.getPath()) + .handler( + ctx -> + handle( + ctx, + EXCEPTION, + () -> { + throw new IllegalStateException(EXCEPTION.getBody()); + })); + + vertx + .createHttpServer() + .requestHandler(router) + .listen(port) + .onComplete(httpServerAsyncResult -> startFuture.complete()); + } + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java new file mode 100644 index 000000000000..b3cd1ad73637 --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/AbstractVertxRxVerticle.java @@ -0,0 +1,110 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.server; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.vertx.reactivex.core.AbstractVerticle; +import io.vertx.reactivex.ext.web.Router; +import io.vertx.reactivex.ext.web.RoutingContext; + +abstract class AbstractVertxRxVerticle extends AbstractVerticle { + + abstract void handle(RoutingContext ctx, ServerEndpoint endpoint, Runnable action); + + void configure(Router router) { + router + .route(SUCCESS.getPath()) + .handler( + ctx -> + handle( + ctx, + SUCCESS, + () -> + ctx.response().setStatusCode(SUCCESS.getStatus()).end(SUCCESS.getBody()))); + + router + .route(INDEXED_CHILD.getPath()) + .handler( + ctx -> + handle( + ctx, + INDEXED_CHILD, + () -> { + INDEXED_CHILD.collectSpanAttributes( + parameter -> ctx.request().params().get(parameter)); + ctx.response() + .setStatusCode(INDEXED_CHILD.getStatus()) + .end(INDEXED_CHILD.getBody()); + })); + + router + .route(QUERY_PARAM.getPath()) + .handler( + ctx -> + handle( + ctx, + QUERY_PARAM, + () -> + ctx.response() + .setStatusCode(QUERY_PARAM.getStatus()) + .end(ctx.request().query()))); + + router + .route(REDIRECT.getPath()) + .handler( + ctx -> + handle( + ctx, + REDIRECT, + () -> + ctx.response() + .setStatusCode(REDIRECT.getStatus()) + .putHeader("location", REDIRECT.getBody()) + .end())); + + router + .route(ERROR.getPath()) + .handler( + ctx -> + handle( + ctx, + ERROR, + () -> ctx.response().setStatusCode(ERROR.getStatus()).end(ERROR.getBody()))); + + router + .route("/path/:id/param") + .handler( + ctx -> + handle( + ctx, + PATH_PARAM, + () -> + ctx.response() + .setStatusCode(PATH_PARAM.getStatus()) + .end(ctx.request().getParam("id")))); + + router + .route(CAPTURE_HEADERS.getPath()) + .handler( + ctx -> + handle( + ctx, + CAPTURE_HEADERS, + () -> + ctx.response() + .setStatusCode(CAPTURE_HEADERS.getStatus()) + .putHeader("X-Test-Response", ctx.request().getHeader("X-Test-Request")) + .end(CAPTURE_HEADERS.getBody()))); + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java new file mode 100644 index 000000000000..0d830e9c2a0a --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactivePropagationTest.java @@ -0,0 +1,229 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.server; + +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static io.opentelemetry.javaagent.instrumentation.vertx.reactive.server.VertxReactiveWebServer.TEST_REQUEST_ID_ATTRIBUTE; +import static io.opentelemetry.javaagent.instrumentation.vertx.reactive.server.VertxReactiveWebServer.TEST_REQUEST_ID_PARAMETER; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.ClientAttributes.CLIENT_ADDRESS; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_ROUTE; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PROTOCOL_VERSION; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.UrlAttributes.URL_PATH; +import static io.opentelemetry.semconv.UrlAttributes.URL_QUERY; +import static io.opentelemetry.semconv.UrlAttributes.URL_SCHEME; +import static io.opentelemetry.semconv.UserAgentAttributes.USER_AGENT_ORIGINAL; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SQL_TABLE; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import io.opentelemetry.testing.internal.armeria.client.WebClient; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpRequest; +import io.opentelemetry.testing.internal.armeria.common.HttpRequestBuilder; +import io.vertx.reactivex.core.Vertx; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class VertxReactivePropagationTest { + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create(); + + private static WebClient client; + private static int port; + private static Vertx server; + + @BeforeAll + static void setUp() throws ExecutionException, InterruptedException, TimeoutException { + port = PortUtils.findOpenPort(); + server = VertxReactiveWebServer.start(port); + client = WebClient.of("h1c://localhost:" + port); + } + + @AfterAll + static void cleanUp() { + server.close(); + } + + // Verifies that context is correctly propagated and sql query span has correct parent. + // Tests io.opentelemetry.javaagent.instrumentation.vertx.reactive.VertxRxInstrumentation + @SuppressWarnings("deprecation") // uses deprecated db semconv + @Test + void contextPropagation() { + AggregatedHttpResponse response = client.get("/listProducts").aggregate().join(); + assertThat(response.status().code()).isEqualTo(SUCCESS.getStatus()); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET /listProducts") + .hasKind(SpanKind.SERVER) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), + satisfies(NETWORK_PEER_PORT, val -> val.isInstanceOf(Long.class)), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, port), + equalTo(CLIENT_ADDRESS, "127.0.0.1"), + equalTo(URL_PATH, "/listProducts"), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(URL_SCHEME, "http"), + satisfies(USER_AGENT_ORIGINAL, val -> val.isInstanceOf(String.class)), + equalTo(HTTP_ROUTE, "/listProducts")), + span -> + span.hasName("handleListProducts") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("listProducts") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)), + span -> + span.hasName("SELECT products") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(2)) + .hasAttributesSatisfyingExactly( + equalTo( + maybeStable(DB_STATEMENT), + "SELECT id, name, price, weight FROM products"), + equalTo(maybeStable(DB_OPERATION), "SELECT"), + equalTo(maybeStable(DB_SQL_TABLE), "products")))); + } + + @SuppressWarnings("deprecation") // uses deprecated db semconv + @Test + void highConcurrency() { + int count = 100; + String baseUrl = "/listProducts"; + CountDownLatch latch = new CountDownLatch(1); + + ExecutorService pool = Executors.newFixedThreadPool(8); + cleanup.deferCleanup(pool::shutdownNow); + TextMapPropagator propagator = GlobalOpenTelemetry.getPropagators().getTextMapPropagator(); + TextMapSetter setter = + (carrier, name, value) -> carrier.header(name, value); + + for (int i = 0; i < count; i++) { + int index = i; + pool.submit( + () -> { + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + testing.runWithSpan( + "client " + index, + () -> { + HttpRequestBuilder builder = + HttpRequest.builder() + .get(baseUrl + "?" + TEST_REQUEST_ID_PARAMETER + "=" + index); + Span.current().setAttribute(TEST_REQUEST_ID_ATTRIBUTE, index); + propagator.inject(Context.current(), builder, setter); + client.execute(builder.build()).aggregate().join(); + }); + }); + } + + latch.countDown(); + + List> assertions = new ArrayList<>(); + for (int i = 0; i < count; i++) { + assertions.add( + trace -> { + long requestId = + Long.parseLong(trace.getSpan(0).getName().substring("client ".length())); + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("client " + requestId) + .hasKind(SpanKind.INTERNAL) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(longKey(TEST_REQUEST_ID_ATTRIBUTE), requestId)), + span -> + span.hasName("GET /listProducts") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), + satisfies(NETWORK_PEER_PORT, val -> val.isInstanceOf(Long.class)), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, port), + equalTo(CLIENT_ADDRESS, "127.0.0.1"), + equalTo(URL_PATH, baseUrl), + equalTo(URL_QUERY, TEST_REQUEST_ID_PARAMETER + "=" + requestId), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(URL_SCHEME, "http"), + satisfies(USER_AGENT_ORIGINAL, val -> val.isInstanceOf(String.class)), + equalTo(HTTP_ROUTE, "/listProducts"), + equalTo(longKey(TEST_REQUEST_ID_ATTRIBUTE), requestId)), + span -> + span.hasName("handleListProducts") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(longKey(TEST_REQUEST_ID_ATTRIBUTE), requestId)), + span -> + span.hasName("listProducts") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)) + .hasAttributesSatisfyingExactly( + equalTo(longKey(TEST_REQUEST_ID_ATTRIBUTE), requestId)), + span -> + span.hasName("SELECT products") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(3)) + .hasAttributesSatisfyingExactly( + equalTo( + maybeStable(DB_STATEMENT), + "SELECT id AS request" + + requestId + + ", name, price, weight FROM products"), + equalTo(maybeStable(DB_OPERATION), "SELECT"), + equalTo(maybeStable(DB_SQL_TABLE), "products"))); + }); + } + testing.waitAndAssertTraces(assertions); + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java new file mode 100644 index 000000000000..b2e569281e9a --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxReactiveWebServer.java @@ -0,0 +1,197 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.server; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.reactivex.Single; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import io.vertx.core.VertxOptions; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.jdbcclient.JDBCConnectOptions; +import io.vertx.reactivex.core.AbstractVerticle; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.core.http.HttpServerResponse; +import io.vertx.reactivex.ext.web.Router; +import io.vertx.reactivex.ext.web.RoutingContext; +import io.vertx.reactivex.jdbcclient.JDBCPool; +import io.vertx.reactivex.sqlclient.SqlConnection; +import io.vertx.sqlclient.PoolOptions; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VertxReactiveWebServer extends AbstractVerticle { + + private static final Logger logger = LoggerFactory.getLogger(VertxReactiveWebServer.class); + + private static final Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + + public static final String TEST_REQUEST_ID_PARAMETER = "test-request-id"; + public static final String TEST_REQUEST_ID_ATTRIBUTE = "test.request.id"; + + private static final String CONFIG_HTTP_SERVER_PORT = "http.server.port"; + private static io.vertx.reactivex.sqlclient.Pool client; + + public static Vertx start(int port) + throws ExecutionException, InterruptedException, TimeoutException { + /* This is highly against Vertx ideas, but our tests are synchronous + so we have to make sure server is up and running */ + CompletableFuture future = new CompletableFuture<>(); + + Vertx server = Vertx.newInstance(io.vertx.core.Vertx.vertx(new VertxOptions())); + client = + JDBCPool.pool( + server, + new JDBCConnectOptions().setJdbcUrl("jdbc:hsqldb:mem:test?shutdown=true"), + new PoolOptions()); + + logger.info("Starting on port {}", port); + server + .deployVerticle( + VertxReactiveWebServer.class.getName(), + new DeploymentOptions().setConfig(new JsonObject().put(CONFIG_HTTP_SERVER_PORT, port))) + .onComplete( + res -> { + if (!res.succeeded()) { + RuntimeException exception = + new RuntimeException("Cannot deploy server Verticle", res.cause()); + future.completeExceptionally(exception); + } + future.complete(null); + }); + + // block until vertx server is up + future.get(30, TimeUnit.SECONDS); + + return server; + } + + @Override + public void start(Promise startPromise) { + setUpInitialData( + ready -> { + Router router = Router.router(vertx); + int port = config().getInteger(CONFIG_HTTP_SERVER_PORT); + logger.info("Listening on port {}", port); + router + .route(SUCCESS.getPath()) + .handler( + ctx -> ctx.response().setStatusCode(SUCCESS.getStatus()).end(SUCCESS.getBody())); + + router.route("/listProducts").handler(VertxReactiveWebServer::handleListProducts); + + vertx + .createHttpServer() + .requestHandler(router) + .listen(port) + .onComplete(it -> startPromise.complete()); + }); + } + + @SuppressWarnings("CheckReturnValue") + private static void handleListProducts(RoutingContext routingContext) { + Long requestId = extractRequestId(routingContext); + attachRequestIdToCurrentSpan(requestId); + + Span span = tracer.spanBuilder("handleListProducts").startSpan(); + try (Scope ignored = Context.current().with(span).makeCurrent()) { + attachRequestIdToCurrentSpan(requestId); + + HttpServerResponse response = routingContext.response(); + Single jsonArraySingle = listProducts(requestId); + + jsonArraySingle.subscribe( + arr -> response.putHeader("content-type", "application/json").end(arr.encode())); + } finally { + span.end(); + } + } + + private static Single listProducts(Long requestId) { + Span span = tracer.spanBuilder("listProducts").startSpan(); + try (Scope ignored = Context.current().with(span).makeCurrent()) { + attachRequestIdToCurrentSpan(requestId); + String queryInfix = requestId != null ? " AS request" + requestId : ""; + + return client + .query("SELECT id" + queryInfix + ", name, price, weight FROM products") + .rxExecute() + .flatMap( + result -> { + JsonArray arr = new JsonArray(); + result.forEach( + row -> { + JsonArray values = new JsonArray(); + for (int i = 0; i < 4; i++) { + values.add(row.getValue(i)); + } + arr.add(values); + }); + return Single.just(arr); + }); + } finally { + span.end(); + } + } + + private static Long extractRequestId(RoutingContext routingContext) { + String requestIdString = routingContext.request().params().get(TEST_REQUEST_ID_PARAMETER); + return requestIdString != null ? Long.valueOf(requestIdString) : null; + } + + private static void attachRequestIdToCurrentSpan(Long requestId) { + if (requestId != null) { + Span.current().setAttribute(TEST_REQUEST_ID_ATTRIBUTE, requestId); + } + } + + private static void setUpInitialData(Handler done) { + client + .getConnection() + .onComplete( + res -> { + if (res.failed()) { + throw new IllegalStateException(res.cause()); + } + + SqlConnection conn = res.result(); + + conn.query( + "CREATE TABLE IF NOT EXISTS products(id INT IDENTITY, name VARCHAR(255), price FLOAT, weight INT)") + .execute() + .onComplete( + ddl -> { + if (ddl.failed()) { + throw new IllegalStateException(ddl.cause()); + } + + conn.query( + "INSERT INTO products (name, price, weight) VALUES ('Egg Whisk', 3.99, 150), ('Tea Cosy', 5.99, 100), ('Spatula', 1.00, 80)") + .execute() + .onComplete( + fixtures -> { + if (fixtures.failed()) { + throw new IllegalStateException(fixtures.cause()); + } + + done.handle(null); + }); + }); + }); + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java new file mode 100644 index 000000000000..e1562b5b42b1 --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxCircuitBreakerHttpServerTest.java @@ -0,0 +1,98 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.server; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.core.Promise; +import io.vertx.reactivex.circuitbreaker.CircuitBreaker; +import io.vertx.reactivex.core.AbstractVerticle; +import io.vertx.reactivex.ext.web.Router; +import io.vertx.reactivex.ext.web.RoutingContext; +import org.junit.jupiter.api.extension.RegisterExtension; + +class VertxRxCircuitBreakerHttpServerTest extends AbstractVertxRxHttpServerTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + + options.setTestHttpPipelining(false); + options.setHasExceptionOnServerSpan(endpoint -> endpoint != EXCEPTION); + } + + @Override + protected Class verticle() { + return VertxRxCircuitBreakerWebTestServer.class; + } + + public static class VertxRxCircuitBreakerWebTestServer extends AbstractVertxRxVerticle { + CircuitBreaker breaker; + + @Override + void handle(RoutingContext ctx, ServerEndpoint endpoint, Runnable action) { + breaker + .execute(future -> future.complete(endpoint)) + .onComplete( + result -> { + if (result.failed()) { + throw new IllegalStateException(result.cause()); + } + controller(endpoint, action::run); + }); + } + + @Override + public void start(Promise startPromise) { + int port = config().getInteger(CONFIG_HTTP_SERVER_PORT); + Router router = Router.router(vertx); + breaker = + CircuitBreaker.create( + "my-circuit-breaker", + vertx, + // Disable the timeout otherwise it makes each test take this long. + new CircuitBreakerOptions().setTimeout(-1)); + + configure(router); + router + .route(EXCEPTION.getPath()) + .handler( + ctx -> + breaker + .execute( + future -> future.fail(new IllegalStateException(EXCEPTION.getBody()))) + .onComplete( + result -> { + try { + Throwable cause = result.cause(); + controller( + EXCEPTION, + () -> { + throw cause; + }); + } catch (Throwable throwable) { + ctx.response() + .setStatusCode(EXCEPTION.getStatus()) + .end(throwable.getMessage()); + } + })); + + vertx + .createHttpServer() + .requestHandler(router) + .listen(port) + .onComplete(httpServerAsyncResult -> startPromise.complete()); + } + } +} diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java new file mode 100644 index 000000000000..c3958e1285f8 --- /dev/null +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version5Test/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/server/VertxRxHttpServerTest.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.vertx.reactive.server; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.vertx.core.Promise; +import io.vertx.reactivex.core.AbstractVerticle; +import io.vertx.reactivex.ext.web.Router; +import io.vertx.reactivex.ext.web.RoutingContext; +import org.junit.jupiter.api.extension.RegisterExtension; + +class VertxRxHttpServerTest extends AbstractVertxRxHttpServerTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); + + @Override + protected Class verticle() { + return VertxReactiveWebServer.class; + } + + public static class VertxReactiveWebServer extends AbstractVertxRxVerticle { + @Override + void handle(RoutingContext ctx, ServerEndpoint endpoint, Runnable action) { + controller(endpoint, action::run); + } + + @Override + public void start(Promise startFuture) { + int port = config().getInteger(CONFIG_HTTP_SERVER_PORT); + Router router = Router.router(vertx); + + configure(router); + router + .route(EXCEPTION.getPath()) + .handler( + ctx -> + handle( + ctx, + EXCEPTION, + () -> { + throw new IllegalStateException(EXCEPTION.getBody()); + })); + + vertx + .createHttpServer() + .requestHandler(router) + .listen(port) + .onComplete(httpServerAsyncResult -> startFuture.complete()); + } + } +} diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index e179c9de3e49..059bae7e583d 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -87,6 +87,18 @@ testing { // Used by byte-buddy but not brought in as a transitive dependency. compileOnly("com.google.code.findbugs:annotations") } + targets { + all { + testTask.configure { + filter { + // Helper class used in test that refers to a class that is missing from the test + // classpath. We need to exclude it to avoid junit failing while it is searching for + // test classes. + excludeTestsMatching("MissingTypeTest\$SomeClass") + } + } + } + } } val testPatchBytecodeVersion by registering(JvmTestSuite::class) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 52de975cc22f..b8c419a9a5e0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,7 @@ pluginManagement { } plugins { - id("com.gradle.common-custom-user-data-gradle-plugin") version "2.2.1" + id("com.gradle.common-custom-user-data-gradle-plugin") version "2.3" id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" // this can't live in pluginManagement currently due to // https://github.com/bmuschko/gradle-docker-plugin/issues/1123 diff --git a/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts index 0f1ccf155b0a..ea5c0e71c9bc 100644 --- a/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts @@ -75,4 +75,28 @@ graalvmNative { useJUnitPlatform() setForkEvery(1) } + + // see https://github.com/junit-team/junit5/wiki/Upgrading-to-JUnit-5.13 + // should not be needed after updating native build tools to 0.11.0 + val initializeAtBuildTime = listOf( + "org.junit.jupiter.api.DisplayNameGenerator\$IndicativeSentences", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$ClassInfo", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$LifecycleMethods", + "org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor", + "org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor", + "org.junit.jupiter.engine.descriptor.DynamicDescendantFilter\$Mode", + "org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector\$1", + "org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor\$MethodInfo", + "org.junit.jupiter.engine.discovery.ClassSelectorResolver\$DummyClassTemplateInvocationContext", + "org.junit.platform.engine.support.store.NamespacedHierarchicalStore\$EvaluatedValue", + "org.junit.platform.launcher.core.DiscoveryIssueNotifier", + "org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider", + "org.junit.platform.launcher.core.LauncherDiscoveryResult\$EngineResultInfo", + "org.junit.platform.suite.engine.SuiteTestDescriptor\$LifecycleMethods", + ) + binaries { + named("test") { + buildArgs.add("--initialize-at-build-time=${initializeAtBuildTime.joinToString(",")}") + } + } } diff --git a/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts index 1b7b865e7b7d..9671dc8bcc43 100644 --- a/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts @@ -78,4 +78,28 @@ graalvmNative { useJUnitPlatform() setForkEvery(1) } + + // see https://github.com/junit-team/junit5/wiki/Upgrading-to-JUnit-5.13 + // should not be needed after updating native build tools to 0.11.0 + val initializeAtBuildTime = listOf( + "org.junit.jupiter.api.DisplayNameGenerator\$IndicativeSentences", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$ClassInfo", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$LifecycleMethods", + "org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor", + "org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor", + "org.junit.jupiter.engine.descriptor.DynamicDescendantFilter\$Mode", + "org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector\$1", + "org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor\$MethodInfo", + "org.junit.jupiter.engine.discovery.ClassSelectorResolver\$DummyClassTemplateInvocationContext", + "org.junit.platform.engine.support.store.NamespacedHierarchicalStore\$EvaluatedValue", + "org.junit.platform.launcher.core.DiscoveryIssueNotifier", + "org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider", + "org.junit.platform.launcher.core.LauncherDiscoveryResult\$EngineResultInfo", + "org.junit.platform.suite.engine.SuiteTestDescriptor\$LifecycleMethods", + ) + binaries { + named("test") { + buildArgs.add("--initialize-at-build-time=${initializeAtBuildTime.joinToString(",")}") + } + } } diff --git a/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts index 64049fbe61fb..71e0daffc54b 100644 --- a/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts @@ -69,4 +69,28 @@ graalvmNative { useJUnitPlatform() setForkEvery(1) } + + // see https://github.com/junit-team/junit5/wiki/Upgrading-to-JUnit-5.13 + // should not be needed after updating native build tools to 0.11.0 + val initializeAtBuildTime = listOf( + "org.junit.jupiter.api.DisplayNameGenerator\$IndicativeSentences", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$ClassInfo", + "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor\$LifecycleMethods", + "org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor", + "org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor", + "org.junit.jupiter.engine.descriptor.DynamicDescendantFilter\$Mode", + "org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector\$1", + "org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor\$MethodInfo", + "org.junit.jupiter.engine.discovery.ClassSelectorResolver\$DummyClassTemplateInvocationContext", + "org.junit.platform.engine.support.store.NamespacedHierarchicalStore\$EvaluatedValue", + "org.junit.platform.launcher.core.DiscoveryIssueNotifier", + "org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider", + "org.junit.platform.launcher.core.LauncherDiscoveryResult\$EngineResultInfo", + "org.junit.platform.suite.engine.SuiteTestDescriptor\$LifecycleMethods", + ) + binaries { + named("test") { + buildArgs.add("--initialize-at-build-time=${initializeAtBuildTime.joinToString(",")}") + } + } } diff --git a/test-report/build.gradle.kts b/test-report/build.gradle.kts index 321e0dd8a0c6..9a23d3edc2cd 100644 --- a/test-report/build.gradle.kts +++ b/test-report/build.gradle.kts @@ -5,7 +5,7 @@ plugins { dependencies { implementation("com.google.api-client:google-api-client:2.8.0") implementation("com.google.apis:google-api-services-sheets:v4-rev20250513-2.0.0") - implementation("com.google.auth:google-auth-library-oauth2-http:1.36.0") + implementation("com.google.auth:google-auth-library-oauth2-http:1.37.0") } otelJava { diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java index 088693ad3b77..8cae71b0684c 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java @@ -45,16 +45,6 @@ */ public abstract class InstrumentationTestRunner { - static { - // In netty-4.1.121.Final unsafe usage is disabled by default on jdk24. This triggers using - // a different pooled buffer implementation which apparently breaks when initializing ssl. Here - // we switch to unpooled buffers to avoid that bug. We do this here because this code is - // executed early for both agent and library tests. - if (Double.parseDouble(System.getProperty("java.specification.version")) >= 24) { - System.setProperty("io.opentelemetry.testing.internal.io.netty.allocator.type", "unpooled"); - } - } - private final TestInstrumenters testInstrumenters; protected Map metrics = new HashMap<>();