Compare commits

..

3 Commits

Author SHA1 Message Date
Trask Stalnaker 8b91a02b87
[release/v2.17.x] Increase nexus publishing client timeout (#14076) 2025-06-20 00:07:07 +00:00
otelbot[bot] b79a813fb4
[release/v2.17.x] Use faster machine for release (#14074)
Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
2025-06-19 20:52:02 +00:00
otelbot[bot] b8d25b8112
[release/v2.17.x] Prepare release 2.17.0 (#14064)
Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com>
2025-06-18 17:41:50 +00:00
242 changed files with 2810 additions and 5115 deletions

View File

@ -1,3 +0,0 @@
# this file exists so that Renovate can auto-update docker image versions that are then used elsewhere
FROM lycheeverse/lychee:sha-2aa22f8@sha256:2e3786630482c41f9f2dd081e06d7da1c36d66996e8cf6573409b8bc418d48c4 AS lychee

View File

@ -1,35 +0,0 @@
#!/bin/bash
set -e
export MSYS_NO_PATHCONV=1 # for Git Bash on Windows
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LYCHEE_CONFIG="$SCRIPT_DIR/../../.lychee.toml"
DEPENDENCIES_DOCKERFILE="$SCRIPT_DIR/dependencies.dockerfile"
# Extract lychee version from dependencies.dockerfile
LYCHEE_VERSION=$(grep "FROM lycheeverse/lychee:" "$DEPENDENCIES_DOCKERFILE" | sed 's/.*FROM lycheeverse\/lychee:\([^ ]*\).*/\1/')
# Build the lychee command with optional GitHub token
CMD="lycheeverse/lychee:$LYCHEE_VERSION --verbose --config $(basename "$LYCHEE_CONFIG")"
# Add GitHub token if available
if [[ -n "$GITHUB_TOKEN" ]]; then
CMD="$CMD --github-token $GITHUB_TOKEN"
fi
# Add the target directory
CMD="$CMD ."
# Determine if we should allocate a TTY
DOCKER_FLAGS="--rm --init"
if [[ -t 0 ]]; then
DOCKER_FLAGS="$DOCKER_FLAGS -it"
else
DOCKER_FLAGS="$DOCKER_FLAGS -i"
fi
# Run lychee with proper signal handling
# shellcheck disable=SC2086
exec docker run $DOCKER_FLAGS -v "$(dirname "$LYCHEE_CONFIG")":/data -w /data $CMD

View File

@ -27,7 +27,7 @@ jobs:
# muzzle is not included here because it doesn't use gradle cache anyway and so is already covered
# by the normal daily build
# link-check and misspell-check are not included here because they don't use gradle cache
# markdown-link-check and misspell-check are not included here because they don't use gradle cache
# anyway and so are already covered by the normal daily build
workflow-notification:

View File

@ -26,8 +26,8 @@ jobs:
shell-script-check:
uses: ./.github/workflows/reusable-shell-script-check.yml
link-check:
uses: ./.github/workflows/reusable-link-check.yml
markdown-link-check:
uses: ./.github/workflows/reusable-markdown-link-check.yml
markdown-lint-check:
uses: ./.github/workflows/reusable-markdown-lint-check.yml
@ -43,7 +43,7 @@ jobs:
- common
- test-latest-deps
- muzzle
- link-check
- markdown-link-check
- misspell-check
if: always()
uses: ./.github/workflows/reusable-workflow-notification.yml
@ -53,6 +53,6 @@ jobs:
needs.common.result == 'success' &&
needs.test-latest-deps.result == 'success' &&
needs.muzzle.result == 'success' &&
needs.link-check.result == 'success' &&
needs.markdown-link-check.result == 'success' &&
needs.misspell-check.result == 'success'
}}

View File

@ -43,11 +43,11 @@ jobs:
uses: ./.github/workflows/reusable-shell-script-check.yml
# this is not a required check to avoid blocking pull requests if external links break
markdown-check:
markdown-link-check:
# release branches are excluded because the README.md javaagent download link has to be updated
# on release branches before the release download has been published
if: "!startsWith(github.ref_name, 'release/') && !startsWith(github.base_ref, 'release/')"
uses: ./.github/workflows/reusable-link-check.yml
uses: ./.github/workflows/reusable-markdown-link-check.yml
markdown-lint-check:
uses: ./.github/workflows/reusable-markdown-lint-check.yml

View File

@ -37,12 +37,12 @@ jobs:
if: "!startsWith(github.ref_name, 'release/')"
uses: ./.github/workflows/reusable-shell-script-check.yml
link-check:
markdown-link-check:
# release branches are excluded to avoid unnecessary maintenance if external links break
# (and also because the README.md javaagent download link has to be updated on release branches
# before the release download has been published)
if: "!startsWith(github.ref_name, 'release/')"
uses: ./.github/workflows/reusable-link-check.yml
uses: ./.github/workflows/reusable-markdown-link-check.yml
markdown-lint-check:
# release branches are excluded
@ -58,7 +58,7 @@ jobs:
publish-snapshots:
needs:
# intentionally not blocking snapshot publishing on test-latest-deps, muzzle,
# link-check, or misspell-check
# markdown-link-check, or misspell-check
- common
runs-on: ubuntu-latest
# skipping release branches because the versions in those branches are not snapshots

View File

@ -57,7 +57,7 @@ jobs:
cache-read-only: ${{ github.event_name == 'pull_request' }}
- name: Initialize CodeQL
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with:
languages: ${{ matrix.language }}
# using "latest" helps to keep up with the latest Kotlin support
@ -73,6 +73,6 @@ jobs:
run: ./gradlew assemble -x javadoc -x :instrumentation:quarkus-resteasy-reactive:quarkus3-testing:quarkusGenerateCodeDev -x :instrumentation:quarkus-resteasy-reactive:quarkus2-testing:quarkusGenerateCodeDev --no-build-cache --no-daemon
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with:
category: "/language:${{matrix.language}}"

View File

@ -42,6 +42,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with:
sarif_file: results.sarif

View File

@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/setup-buildx-action@18ce135bb5112fa8ce4ed6c17ab05699d7f3a5e0 # v3.11.0
- name: Login to GitHub container registry
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0

View File

@ -21,7 +21,7 @@ jobs:
release:
permissions:
contents: write # for creating the release
runs-on: ubuntu-latest
runs-on: oracle-8cpu-32gb-x86-64
needs:
- common
outputs:

View File

@ -1,18 +0,0 @@
name: Reusable - Link check
on:
workflow_call:
permissions:
contents: read
jobs:
link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Link check
env:
GITHUB_TOKEN: ${{ github.token }}
run: ./.github/scripts/link-check.sh

View File

@ -0,0 +1,27 @@
name: Reusable - Markdown link check
on:
workflow_call:
permissions:
contents: read
jobs:
markdown-link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2.4.1
with:
# excluding links to pull requests and issues is done for performance
# stackexchange link fails with 403 when accessed by lychee
args: >
--include-fragments
--exclude "^https://github.com/open-telemetry/opentelemetry-java-instrumentation/(issues|pull)/\\d+$"
--exclude "^http://code.google.com/p/concurrentlinkedhashmap$"
--exclude "^https://softwareengineering.stackexchange.com/questions/29727"
--max-retries 6
--max-concurrency 4
--github-token ${{ github.token }}
.

View File

@ -14,7 +14,9 @@ jobs:
- name: Install misspell
run: |
curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | sh -s -- -b bin
curl -L -o install-misspell.sh \
https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh
sh ./install-misspell.sh
- name: Run misspell
run: |

View File

@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- id: read-java
run: echo "version=$(cat .java-version)" >> "$GITHUB_OUTPUT"
- uses: graalvm/setup-graalvm@e1df20a713a4cc6ab5b0eb03f0e0dcdc0199b805 # v1.3.4.1
- uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # v1.3.3.1
with:
version: "latest"
java-version: ${{ matrix.test-java-version }}

1
.gitignore vendored
View File

@ -58,7 +58,6 @@ hs_err_pid*
replay_pid*
.attach_pid*
.telemetry*
.lycheecache
!java-agent/benchmark/releases/*.jar

View File

@ -1,16 +0,0 @@
timeout = 30
retry_wait_time = 5
max_retries = 6
max_concurrency = 4
# Check link anchors
include_fragments = true
# excluding links to pull requests and issues is done for performance
# stackexchange link fails with 403 when accessed by lychee
exclude = [
'^https://github.com/open-telemetry/opentelemetry-java-instrumentation/(issues|pull)/\d+$',
'^http://code.google.com/p/concurrentlinkedhashmap$',
'^https://softwareengineering.stackexchange.com/questions/29727.*',
'^https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/io/opentelemetry/$',
]

View File

@ -1,8 +1,6 @@
# Changelog
## Unreleased
## Version 2.17.0 (2025-06-20)
## Version 2.17.0 (2025-06-18)
### Migration notes

View File

@ -33,7 +33,7 @@ If you are looking for documentation on using those.
## Getting Started
Download
the [latest version](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar).
the [latest version](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.17.0/opentelemetry-javaagent.jar).
This package includes the instrumentation agent as well as
instrumentations for all supported libraries and all available data exporters.
@ -129,39 +129,36 @@ Debug logging negatively impacts the performance of your application.
See [CONTRIBUTING.md](CONTRIBUTING.md).
### Maintainers
- [Lauri Tulmin](https://github.com/laurit), Splunk
- [Trask Stalnaker](https://github.com/trask), Microsoft
For more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer).
### Approvers
Approvers ([@open-telemetry/java-instrumentation-approvers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-approvers)):
- [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana
- [Jack Berg](https://github.com/jack-berg), New Relic
- [Jason Plumb](https://github.com/breedx-splk), Splunk
- [Jay DeLuca](https://github.com/jaydeluca), Grafana
- [Jay DeLuca](https://github.com/jaydeluca)
- [Jean Bisutti](https://github.com/jeanbisutti), Microsoft
- [John Watson](https://github.com/jkwatson), Cloudera
- [Jonas Kunz](https://github.com/JonasKunz), Elastic
- [Steve Rao](https://github.com/steverao), Alibaba
- [Sylvain Juge](https://github.com/SylvainJuge), Elastic
For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver).
Maintainers ([@open-telemetry/java-instrumentation-maintainers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-maintainers)):
### Emeritus maintainers
- [Lauri Tulmin](https://github.com/laurit), Splunk
- [Trask Stalnaker](https://github.com/trask), Microsoft
Emeritus maintainers:
- [Mateusz Rzeszutek](https://github.com/mateuszrzeszutek)
- [Nikita Salnikov-Tarnovski](https://github.com/iNikem)
- [Tyler Benson](https://github.com/tylerbenson)
For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
Learn more about roles in
the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md).
### Thanks to all of our contributors!
Thanks to all the people who have already contributed!
<a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/graphs/contributors">
<img alt="Repo contributors" src="https://contrib.rocks/image?repo=open-telemetry/opentelemetry-java-instrumentation" />
<img src="https://contributors-img.web.app/image?repo=open-telemetry/opentelemetry-java-instrumentation" />
</a>
[config-agent]: https://opentelemetry.io/docs/zero-code/java/agent/configuration/

View File

@ -13,7 +13,7 @@ otelJava {
}
dependencies {
jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.5.3")
jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.5.0")
}
tasks {

View File

@ -1,4 +1,4 @@
FROM eclipse-temurin:11.0.27_6-jdk@sha256:0296b46d1949f49054497db63a7630d4805ef587c2937eb23a582a2ffdde88da as app-build
FROM eclipse-temurin:11.0.27_6-jdk@sha256:cc77e8b834bfa18f4bf93d1649e27256213490168b35d7c72a2b92545763fab8 as app-build
# This is the base image that will contain a built version of the spring-petclinic-rest
# application. Installing the dependencies and maven compiling the application is time

View File

@ -16,10 +16,10 @@ repositories {
}
dependencies {
implementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
implementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
testImplementation("org.testcontainers:testcontainers:1.21.3")
testImplementation("org.testcontainers:postgresql:1.21.3")
testImplementation("org.testcontainers:testcontainers:1.21.1")
testImplementation("org.testcontainers:postgresql:1.21.1")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testImplementation("com.squareup.okhttp3:okhttp:4.12.0")

View File

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -57,7 +57,7 @@ dependencies {
implementation("com.diffplug.spotless:spotless-plugin-gradle:7.0.4")
implementation("com.google.guava:guava:33.4.8-jre")
implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18")
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.8")
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.6")
implementation("org.apache.httpcomponents:httpclient:4.5.14")
implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.0.2")
implementation("org.owasp:dependency-check-gradle:12.1.3")
@ -67,11 +67,11 @@ dependencies {
implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.17.6")
implementation("gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.6")
implementation("me.champeau.jmh:jmh-gradle-plugin:0.7.3")
implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.3.0")
implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.2.0")
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.13.3"))
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.assertj:assertj-core:3.27.3")

View File

@ -431,7 +431,7 @@ codenarc {
checkstyle {
configFile = rootProject.file("buildscripts/checkstyle.xml")
// this version should match the version of google_checks.xml used as basis for above configuration
toolVersion = "10.26.1"
toolVersion = "10.25.0"
maxWarnings = 0
}

View File

@ -6,7 +6,7 @@ data class DependencySet(val group: String, val version: String, val modules: Li
// this line is managed by .github/scripts/update-sdk-version.sh
val otelSdkVersion = "1.51.0"
val otelContribVersion = "1.47.0-alpha"
val otelContribVersion = "1.46.0-alpha"
val otelSdkAlphaVersion = otelSdkVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1")
// Need both BOM and groovy jars
@ -28,17 +28,17 @@ val DEPENDENCY_BOMS = listOf(
// even if they are only used by test dependencies, so not using junit bom since it is LGPL
"com.fasterxml.jackson:jackson-bom:2.19.1",
"com.squareup.okio:okio-bom:3.15.0", // see https://github.com/open-telemetry/opentelemetry-java/issues/5637
"com.squareup.okio:okio-bom:3.13.0", // see https://github.com/open-telemetry/opentelemetry-java/issues/5637
"com.google.guava:guava-bom:33.4.8-jre",
"org.apache.groovy:groovy-bom:${groovyVersion}",
"io.opentelemetry:opentelemetry-bom:${otelSdkVersion}",
"io.opentelemetry:opentelemetry-bom-alpha:${otelSdkAlphaVersion}",
"org.testcontainers:testcontainers-bom:1.21.3"
"org.testcontainers:testcontainers-bom:1.21.1"
)
val autoServiceVersion = "1.1.1"
val autoValueVersion = "1.11.0"
val errorProneVersion = "2.39.0"
val errorProneVersion = "2.38.0"
val byteBuddyVersion = "1.17.6"
val asmVersion = "9.8"
val jmhVersion = "1.37"
@ -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.13.3",
"org.junit.jupiter:junit-jupiter-api:5.13.1",
"org.spockframework:spock-core:2.4-M6-groovy-4.0",
"org.spockframework:spock-junit4:2.4-M6-groovy-4.0",

View File

@ -1,2 +0,0 @@
Comparing source compatibility of opentelemetry-instrumentation-annotations-2.17.0.jar against opentelemetry-instrumentation-annotations-2.16.0.jar
No changes.

View File

@ -1,2 +0,0 @@
Comparing source compatibility of opentelemetry-instrumentation-api-2.17.0.jar against opentelemetry-instrumentation-api-2.16.0.jar
No changes.

View File

@ -1,2 +0,0 @@
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.17.0.jar against opentelemetry-spring-boot-autoconfigure-2.16.0.jar
No changes.

View File

@ -1,2 +0,0 @@
Comparing source compatibility of opentelemetry-spring-boot-starter-2.17.0.jar against opentelemetry-spring-boot-starter-2.16.0.jar
No changes.

View File

@ -1,2 +1,2 @@
Comparing source compatibility of opentelemetry-instrumentation-annotations-2.18.0-SNAPSHOT.jar against opentelemetry-instrumentation-annotations-2.17.0.jar
Comparing source compatibility of opentelemetry-instrumentation-annotations-2.17.0.jar against opentelemetry-instrumentation-annotations-2.16.0.jar
No changes.

View File

@ -1,2 +1,2 @@
Comparing source compatibility of opentelemetry-instrumentation-api-2.18.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.17.0.jar
Comparing source compatibility of opentelemetry-instrumentation-api-2.17.0.jar against opentelemetry-instrumentation-api-2.16.0.jar
No changes.

View File

@ -1,2 +1,2 @@
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.18.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.17.0.jar
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.17.0.jar against opentelemetry-spring-boot-autoconfigure-2.16.0.jar
No changes.

View File

@ -1,2 +1,2 @@
Comparing source compatibility of opentelemetry-spring-boot-starter-2.18.0-SNAPSHOT.jar against opentelemetry-spring-boot-starter-2.17.0.jar
Comparing source compatibility of opentelemetry-spring-boot-starter-2.17.0.jar against opentelemetry-spring-boot-starter-2.16.0.jar
No changes.

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,8 @@ buildscript {
}
dependencies {
classpath "com.diffplug.spotless:spotless-plugin-gradle:7.0.4"
classpath "com.gradleup.shadow:shadow-gradle-plugin:8.3.8"
classpath "io.opentelemetry.instrumentation:gradle-plugins:2.18.0-alpha-SNAPSHOT"
classpath "com.gradleup.shadow:shadow-gradle-plugin:8.3.6"
classpath "io.opentelemetry.instrumentation:gradle-plugins:2.17.0-alpha"
}
}
@ -30,8 +30,8 @@ subprojects {
opentelemetrySdk : "1.51.0",
// these lines are managed by .github/scripts/update-version.sh
opentelemetryJavaagent : "2.18.0-SNAPSHOT",
opentelemetryJavaagentAlpha: "2.18.0-alpha-SNAPSHOT",
opentelemetryJavaagent : "2.17.0",
opentelemetryJavaagentAlpha: "2.17.0-alpha",
autoservice : "1.1.1"
]
@ -69,7 +69,7 @@ subprojects {
testImplementation("org.mockito:mockito-core:5.18.0")
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

View File

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -3,7 +3,7 @@ plugins {
}
dependencies {
testImplementation("org.testcontainers:testcontainers:1.21.3")
testImplementation("org.testcontainers:testcontainers:1.21.1")
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.19.1")
testImplementation("com.google.protobuf:protobuf-java-util:4.31.1")
testImplementation("com.squareup.okhttp3:okhttp:4.12.0")

View File

@ -10,11 +10,11 @@ plugins {
into a single jar.
See https://imperceptiblethoughts.com/shadow/ for more details about Shadow plugin.
*/
id "com.gradleup.shadow" version "8.3.8"
id "com.gradleup.shadow" version "8.3.6"
id "com.diffplug.spotless" version "7.0.4"
id "io.opentelemetry.instrumentation.muzzle-generation" version "2.18.0-alpha-SNAPSHOT"
id "io.opentelemetry.instrumentation.muzzle-check" version "2.18.0-alpha-SNAPSHOT"
id "io.opentelemetry.instrumentation.muzzle-generation" version "2.17.0-alpha"
id "io.opentelemetry.instrumentation.muzzle-check" version "2.17.0-alpha"
}
group 'io.opentelemetry.example'
@ -26,8 +26,8 @@ ext {
opentelemetrySdk : "1.51.0",
// these lines are managed by .github/scripts/update-version.sh
opentelemetryJavaagent : "2.18.0-SNAPSHOT",
opentelemetryJavaagentAlpha: "2.18.0-alpha-SNAPSHOT"
opentelemetryJavaagent : "2.17.0",
opentelemetryJavaagentAlpha: "2.17.0-alpha"
]
deps = [
@ -97,14 +97,14 @@ dependencies {
implementation 'org.apache.commons:commons-lang3:3.17.0'
//All dependencies below are only for tests
testImplementation("org.testcontainers:testcontainers:1.21.3")
testImplementation("org.testcontainers:testcontainers:1.21.1")
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.19.1")
testImplementation("com.google.protobuf:protobuf-java-util:4.31.1")
testImplementation("com.squareup.okhttp3:okhttp:4.12.0")
testImplementation("io.opentelemetry:opentelemetry-api")
testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha")
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

View File

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -40,11 +40,11 @@ dependencies {
implementation("org.eclipse.aether:aether-transport-http:${aetherVersion}")
implementation("org.apache.maven:maven-aether-provider:3.3.9")
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.8")
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.6")
testImplementation("org.assertj:assertj-core:3.27.3")
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

View File

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -6,20 +6,20 @@
package io.opentelemetry.javaagent.instrumentation.otelannotations;
import static io.opentelemetry.api.common.AttributeKey.booleanKey;
import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFunctionAssertions;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@SuppressWarnings("deprecation") // CodeIncubatingAttributes.CODE_FUNCTION is deprecated
public abstract class AbstractWithSpanTest<T extends U, U> {
@RegisterExtension
@ -59,7 +59,8 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(
codeFunctionAssertions(traced.getClass(), "completable"))));
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
equalTo(CODE_FUNCTION, "completable"))));
}
@Test
@ -81,7 +82,8 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
.hasStatus(StatusData.error())
.hasException(AbstractTraced.FAILURE)
.hasAttributesSatisfyingExactly(
codeFunctionAssertions(traced.getClass(), "completable"))));
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
equalTo(CODE_FUNCTION, "completable"))));
}
@Test
@ -90,10 +92,6 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
T future = traced.completable();
cancel(future);
List<AttributeAssertion> attributeAssertions =
codeFunctionAssertions(traced.getClass(), "completable");
attributeAssertions.add(equalTo(booleanKey(canceledKey()), true));
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
@ -101,7 +99,10 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
span.hasName("Traced.completable")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(attributeAssertions)));
.hasAttributesSatisfyingExactly(
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
equalTo(CODE_FUNCTION, "completable"),
equalTo(booleanKey(canceledKey()), true))));
}
@Test
@ -117,7 +118,8 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(
codeFunctionAssertions(traced.getClass(), "alreadySucceeded"))));
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
equalTo(CODE_FUNCTION, "alreadySucceeded"))));
}
@Test
@ -136,6 +138,7 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
.hasStatus(StatusData.error())
.hasException(AbstractTraced.FAILURE)
.hasAttributesSatisfyingExactly(
codeFunctionAssertions(traced.getClass(), "alreadyFailed"))));
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
equalTo(CODE_FUNCTION, "alreadyFailed"))));
}
}

View File

@ -13,7 +13,7 @@ group = "io.opentelemetry.instrumentation"
dependencies {
api("io.opentelemetry.semconv:opentelemetry-semconv")
api(project(":instrumentation-api"))
api("io.opentelemetry:opentelemetry-api-incubator")
implementation("io.opentelemetry:opentelemetry-api-incubator")
compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")
@ -42,11 +42,11 @@ tasks {
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database,code")
jvmArgs("-Dotel.semconv-stability.opt-in=database")
}
val testBothSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database/dup,code/dup")
jvmArgs("-Dotel.semconv-stability.opt-in=database/dup")
}
check {

View File

@ -7,7 +7,6 @@ package io.opentelemetry.instrumentation.api.incubator.config.internal;
import static java.util.Collections.emptyList;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import java.time.Duration;
import java.util.List;
import java.util.Map;
@ -108,18 +107,4 @@ public interface InstrumentationConfig {
* {@code key=value,anotherKey=anotherValue}. The returned map is unmodifiable.
*/
Map<String, String> getMap(String name, Map<String, String> defaultValue);
/**
* Returns a {@link DeclarativeConfigProperties} for the given instrumentation name, or {@code
* null} if no declarative configuration is available for that instrumentation.
*
* <p>Declarative configuration is used to configure instrumentation properties in a declarative
* way, such as through YAML or JSON files.
*
* @param instrumentationName the name of the instrumentation
* @return the declarative configuration properties for the given instrumentation name, or {@code
* null} if not available
*/
@Nullable
DeclarativeConfigProperties getDeclarativeConfig(String instrumentationName);
}

View File

@ -11,8 +11,6 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.semconv.CodeAttributes;
import javax.annotation.Nullable;
/**
@ -24,9 +22,9 @@ public final class CodeAttributesExtractor<REQUEST, RESPONSE>
implements AttributesExtractor<REQUEST, RESPONSE> {
// copied from CodeIncubatingAttributes
private static final AttributeKey<String> CODE_FUNCTION = AttributeKey.stringKey("code.function");
private static final AttributeKey<String> CODE_NAMESPACE =
AttributeKey.stringKey("code.namespace");
private static final AttributeKey<String> CODE_FUNCTION = AttributeKey.stringKey("code.function");
/** Creates the code attributes extractor. */
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
@ -42,28 +40,11 @@ public final class CodeAttributesExtractor<REQUEST, RESPONSE>
@Override
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
StringBuilder sb = new StringBuilder();
Class<?> cls = getter.getCodeClass(request);
if (cls != null) {
sb.append(cls.getName());
if (SemconvStability.isEmitOldCodeSemconv()) {
internalSet(attributes, CODE_NAMESPACE, cls.getName());
}
}
String methodName = getter.getMethodName(request);
if (methodName != null) {
if (sb.length() > 0) {
sb.append(".");
}
sb.append(methodName);
if (SemconvStability.isEmitOldCodeSemconv()) {
internalSet(attributes, CODE_FUNCTION, methodName);
}
}
if (SemconvStability.isEmitStableCodeSemconv() && sb.length() > 0) {
internalSet(attributes, CodeAttributes.CODE_FUNCTION_NAME, sb.toString());
internalSet(attributes, CODE_NAMESPACE, cls.getName());
}
internalSet(attributes, CODE_FUNCTION, getter.getMethodName(request));
}
@Override

View File

@ -6,14 +6,12 @@
package io.opentelemetry.instrumentation.api.incubator.semconv.code;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil;
import io.opentelemetry.semconv.CodeAttributes;
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes;
import java.util.Collections;
import java.util.HashMap;
@ -60,19 +58,11 @@ class CodeAttributesExtractorTest {
underTest.onEnd(endAttributes, context, request, null, null);
// then
Attributes attributes = startAttributes.build();
SemconvCodeStabilityUtil.codeFunctionAssertions(TestClass.class, "doSomething");
assertThat(startAttributes.build())
.containsOnly(
entry(CodeIncubatingAttributes.CODE_NAMESPACE, TestClass.class.getName()),
entry(CodeIncubatingAttributes.CODE_FUNCTION, "doSomething"));
if (SemconvStability.isEmitStableCodeSemconv()) {
assertThat(attributes)
.containsEntry(
CodeAttributes.CODE_FUNCTION_NAME, TestClass.class.getName() + ".doSomething");
}
if (SemconvStability.isEmitOldCodeSemconv()) {
assertThat(attributes)
.containsEntry(CodeIncubatingAttributes.CODE_NAMESPACE, TestClass.class.getName())
.containsEntry(CodeIncubatingAttributes.CODE_FUNCTION, "doSomething");
}
assertThat(endAttributes.build().isEmpty()).isTrue();
}

View File

@ -21,47 +21,27 @@ public final class SemconvStability {
private static final boolean emitOldDatabaseSemconv;
private static final boolean emitStableDatabaseSemconv;
private static final boolean emitOldCodeSemconv;
private static final boolean emitStableCodeSemconv;
static {
boolean oldDatabase = true;
boolean stableDatabase = false;
boolean oldCode = true;
boolean stableCode = false;
String value = ConfigPropertiesUtil.getString("otel.semconv-stability.opt-in");
if (value != null) {
Set<String> values = new HashSet<>(asList(value.split(",")));
// no else -- technically it's possible to set "XXX,XXX/dup", in which case we
// should emit both sets of attributes for XXX
if (values.contains("database")) {
oldDatabase = false;
stableDatabase = true;
}
// no else -- technically it's possible to set "database,database/dup", in which case we
// should emit both sets of attributes
if (values.contains("database/dup")) {
oldDatabase = true;
stableDatabase = true;
}
if (values.contains("code")) {
oldCode = false;
stableCode = true;
}
if (values.contains("code/dup")) {
oldCode = true;
stableCode = true;
}
}
emitOldDatabaseSemconv = oldDatabase;
emitStableDatabaseSemconv = stableDatabase;
emitOldCodeSemconv = oldCode;
emitStableCodeSemconv = stableCode;
}
public static boolean emitOldDatabaseSemconv() {
@ -97,13 +77,5 @@ public final class SemconvStability {
return dbSystemName != null ? dbSystemName : oldDbSystem;
}
public static boolean isEmitOldCodeSemconv() {
return emitOldCodeSemconv;
}
public static boolean isEmitStableCodeSemconv() {
return emitStableCodeSemconv;
}
private SemconvStability() {}
}

View File

@ -12,7 +12,7 @@ dependencies {
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.1")
implementation("io.opentelemetry:opentelemetry-sdk-common")
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
testImplementation("org.assertj:assertj-core:3.27.3")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")

View File

@ -14,18 +14,13 @@ fi
readonly INSTRUMENTATIONS=(
# <module path (colon-separated)> : <javaagent|library> : [ gradle-task-suffix ]
"activej-http-6.0:javaagent:test"
"akka:akka-http-10.0:javaagent:test"
"apache-httpasyncclient-4.1:javaagent:test"
"alibaba-druid-1.0:javaagent:test"
"alibaba-druid-1.0:javaagent:testStableSemconv"
"apache-dbcp-2.0:javaagent:test"
"apache-dbcp-2.0:javaagent:testStableSemconv"
"apache-httpclient:apache-httpclient-2.0:javaagent:test"
"apache-httpclient:apache-httpclient-4.0:javaagent:test"
"apache-httpclient:apache-httpclient-4.3:library:test"
"apache-httpclient:apache-httpclient-5.0:javaagent:test"
"apache-dubbo-2.7:javaagent:testDubbo"
"c3p0-0.9:javaagent:test"
"c3p0-0.9:javaagent:testStableSemconv"
"clickhouse-client-0.5:javaagent:test"
@ -174,7 +169,7 @@ echo
./gradlew "${gradle_tasks[@]}" \
-PcollectMetadata=true \
--rerun-tasks --continue
--rerun-tasks
# uncomment the next line to remove all .telemetry directories
#find_and_remove_all_telemetry

View File

@ -8,11 +8,10 @@ Run the analysis to update the instrumentation-list.yaml:
`./gradlew :instrumentation-docs:runAnalysis`
### Telemetry collection
### Metric collection
Until this process is ready for all instrumentations, each module will be modified to include a
system property feature flag configured for when the tests run. By enabling the following flag you
will enable metric collection:
system property feature flag configured for when the tests run:
```kotlin
tasks {
@ -23,17 +22,6 @@ tasks {
}
```
In order to collect spans, add the `collectSpans` property (along with `collectMetadata`):
```kotlin
tasks {
test {
systemProperty("collectMetadata", collectMetadata)
systemProperty("collectSpans", true)
}
}
```
Sometimes instrumentation will behave differently based on configuration options, and we can
differentiate between these configurations by using the `metaDataConfig` system property. When the
telemetry is written to a file, the value of this property will be included, or it will default to
@ -142,9 +130,6 @@ public class SpringWebInstrumentationModule extends InstrumentationModule
* metrics
* List of metrics that the instrumentation module collects, including the metric name, description, type, and attributes.
* Separate lists for the metrics emitted by default vs via configuration options.
* spans
* List of spans kinds the instrumentation module generates, including the attributes and their types.
* Separate lists for the spans emitted by default vs via configuration options.
## Methodology
@ -183,16 +168,16 @@ name is determined by the instrumentation module name: `io.opentelemetry.{instr
We will implement gatherers for the schemaUrl and scope attributes when instrumentations start
implementing them.
### Spans and Metrics
### Metrics
In order to identify what telemetry is emitted from instrumentations, we can hook into the
`InstrumentationTestRunner` class and collect the metrics and spans generated during runs. We can then
In order to identify what metrics are emitted from instrumentations, we can hook into the
`InstrumentationTestRunner` class and collect the metrics generated during runs. We can then
leverage the `afterTestClass()` in the Agent and library test runners to then write this information
into temporary files. When we analyze the instrumentation modules, we can read these files and
generate the telemetry section of the instrumentation-list.yaml file.
generate the metrics section of the instrumentation-list.yaml file.
The data is written into a `.telemetry` directory in the root of each instrumentation module. This
data will be excluded from git and just generated on demand.
Each file has a `when` value along with the list of metrics that indicates whether the telemetry is
emitted by default or via a configuration option.
Each file has a `when` value along with the list of metrics that indicates whether the telemetry is emitted by default or via a
configuration option.

View File

@ -14,7 +14,6 @@ import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.TreeMap;
@ -108,48 +107,5 @@ public class DocGeneratorApplication {
+ "%)";
}
@SuppressWarnings("unused") // temporary helper method used for project tracking
private static String listAllModules(List<InstrumentationModule> modules) {
return modules.stream()
.map(InstrumentationModule::getInstrumentationName)
.sorted()
.map(name -> "- [ ] " + name)
.collect(Collectors.joining("\n"));
}
@SuppressWarnings("unused") // temporary helper method used for project tracking
private static String modulesWithDescriptions(List<InstrumentationModule> modules) {
// checklist of all modules sorted by name, with a check if description is set
return modules.stream()
.sorted(Comparator.comparing(InstrumentationModule::getInstrumentationName))
.map(
module -> {
boolean hasDescription =
module.getMetadata() != null
&& module.getMetadata().getDescription() != null
&& !module.getMetadata().getDescription().isEmpty();
String checkbox = hasDescription ? "- [x] " : "- [ ] ";
return checkbox + module.getInstrumentationName();
})
.collect(Collectors.joining("\n"));
}
@SuppressWarnings("unused") // temporary helper method used for project tracking
private static String modulesWithConfigs(List<InstrumentationModule> modules) {
// checklist of all modules sorted by name, with a check if config is set
return modules.stream()
.sorted(Comparator.comparing(InstrumentationModule::getInstrumentationName))
.map(
module -> {
boolean hasDescription =
module.getMetadata() != null
&& module.getMetadata().getConfigurations() != null
&& !module.getMetadata().getConfigurations().isEmpty();
String checkbox = hasDescription ? "- [x] " : "- [ ] ";
return checkbox + module.getInstrumentationName();
})
.collect(Collectors.joining("\n"));
}
private DocGeneratorApplication() {}
}

View File

@ -5,29 +5,26 @@
package io.opentelemetry.instrumentation.docs;
import com.fasterxml.jackson.core.JsonProcessingException;
import static io.opentelemetry.instrumentation.docs.parsers.GradleParser.parseGradleFile;
import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
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.GradleParser;
import io.opentelemetry.instrumentation.docs.parsers.MetricParser;
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
import io.opentelemetry.instrumentation.docs.parsers.SpanParser;
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;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Analyzes instrumentation modules by extracting version information, metrics, spans, and metadata
* from various source files.
*/
class InstrumentationAnalyzer {
private static final Logger logger = Logger.getLogger(InstrumentationAnalyzer.class.getName());
@ -39,52 +36,100 @@ class InstrumentationAnalyzer {
}
/**
* Analyzes all instrumentation modules found in the root directory.
* Converts a list of {@link InstrumentationPath} into a list of {@link InstrumentationModule},
*
* @return a list of analyzed {@link InstrumentationModule}
* @throws IOException if file operations fail
* @param paths the list of {@link InstrumentationPath} objects to be converted
* @return a list of {@link InstrumentationModule} objects with aggregated types
*/
public List<InstrumentationModule> analyze() throws IOException {
List<InstrumentationPath> paths = fileManager.getInstrumentationPaths();
List<InstrumentationModule> modules =
ModuleParser.convertToModules(fileManager.rootDir(), paths);
public static List<InstrumentationModule> convertToInstrumentationModules(
String rootPath, List<InstrumentationPath> paths) {
Map<String, InstrumentationModule> moduleMap = new HashMap<>();
for (InstrumentationModule module : modules) {
enrichModule(module);
for (InstrumentationPath path : paths) {
String key = path.group() + ":" + path.namespace() + ":" + path.instrumentationName();
if (!moduleMap.containsKey(key)) {
moduleMap.put(
key,
new InstrumentationModule.Builder()
.srcPath(sanitizePathName(rootPath, path.srcPath()))
.instrumentationName(path.instrumentationName())
.namespace(path.namespace())
.group(path.group())
.build());
}
}
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, metric data from
* files in the .telemetry directories, and other information from metadata.yaml files.
*
* @return a list of {@link InstrumentationModule}
*/
List<InstrumentationModule> analyze() throws IOException {
List<InstrumentationPath> paths = fileManager.getInstrumentationPaths();
List<InstrumentationModule> modules =
convertToInstrumentationModules(fileManager.rootDir(), paths);
for (InstrumentationModule module : modules) {
List<String> gradleFiles = fileManager.findBuildGradleFiles(module.getSrcPath());
analyzeVersions(gradleFiles, module);
String metadataFile = fileManager.getMetaDataFile(module.getSrcPath());
if (metadataFile != null) {
try {
module.setMetadata(YamlHelper.metaDataParser(metadataFile));
} catch (ValueInstantiationException e) {
logger.severe("Error parsing metadata file for " + module.getInstrumentationName());
throw e;
}
}
Map<String, EmittedMetrics> metrics =
MetricParser.getMetricsFromFiles(fileManager.rootDir(), module.getSrcPath());
for (Map.Entry<String, EmittedMetrics> entry : metrics.entrySet()) {
if (entry.getValue() == null || entry.getValue().getMetrics() == null) {
continue;
}
module.getMetrics().put(entry.getKey(), entry.getValue().getMetrics());
}
}
return modules;
}
private void enrichModule(InstrumentationModule module) throws IOException {
InstrumentationMetaData metaData = getMetadata(module);
if (metaData != null) {
module.setMetadata(metaData);
}
void analyzeVersions(List<String> files, InstrumentationModule module) {
Map<InstrumentationType, Set<String>> versions = new HashMap<>();
for (String file : files) {
String fileContents = FileManager.readFileToString(file);
if (fileContents == null) {
continue;
}
module.setTargetVersions(getVersionInformation(module));
module.setMetrics(MetricParser.getMetrics(module, fileManager));
module.setSpans(SpanParser.getSpans(module, fileManager));
}
DependencyInfo results = null;
@Nullable
private InstrumentationMetaData getMetadata(InstrumentationModule module)
throws JsonProcessingException {
String metadataFile = fileManager.getMetaDataFile(module.getSrcPath());
if (metadataFile == null) {
return null;
if (file.contains("/javaagent/")) {
results = parseGradleFile(fileContents, InstrumentationType.JAVAAGENT);
versions
.computeIfAbsent(InstrumentationType.JAVAAGENT, k -> new HashSet<>())
.addAll(results.versions());
} else if (file.contains("/library/")) {
results = parseGradleFile(fileContents, InstrumentationType.LIBRARY);
versions
.computeIfAbsent(InstrumentationType.LIBRARY, k -> new HashSet<>())
.addAll(results.versions());
}
if (results != null && results.minJavaVersionSupported() != null) {
module.setMinJavaVersion(results.minJavaVersionSupported());
}
}
try {
return YamlHelper.metaDataParser(metadataFile);
} catch (ValueInstantiationException e) {
logger.severe("Error parsing metadata file for " + module.getInstrumentationName());
throw e;
}
}
private Map<InstrumentationType, Set<String>> getVersionInformation(
InstrumentationModule module) {
List<String> gradleFiles = fileManager.findBuildGradleFiles(module.getSrcPath());
return GradleParser.extractVersions(gradleFiles, module);
module.setTargetVersions(versions);
}
}

View File

@ -5,9 +5,6 @@
package io.opentelemetry.instrumentation.docs.internal;
import static java.util.Collections.emptyList;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
@ -19,18 +16,16 @@ import java.util.List;
public class EmittedMetrics {
// Condition in which the metrics are emitted (ex: default, or configuration option names).
private String when;
@JsonProperty("metrics_by_scope")
private List<MetricsByScope> metricsByScope;
private List<Metric> metrics;
public EmittedMetrics() {
this.when = "";
this.metricsByScope = emptyList();
this.metrics = new ArrayList<>();
}
public EmittedMetrics(String when, List<MetricsByScope> metricsByScope) {
this.when = when;
this.metricsByScope = metricsByScope;
public EmittedMetrics(String when, List<Metric> metrics) {
this.when = "";
this.metrics = metrics;
}
public String getWhen() {
@ -41,49 +36,12 @@ public class EmittedMetrics {
this.when = when;
}
@JsonProperty("metrics_by_scope")
public List<MetricsByScope> getMetricsByScope() {
return metricsByScope;
public List<Metric> getMetrics() {
return metrics;
}
@JsonProperty("metrics_by_scope")
public void setMetricsByScope(List<MetricsByScope> metricsByScope) {
this.metricsByScope = metricsByScope;
}
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public static class MetricsByScope {
private String scope;
private List<Metric> metrics;
public MetricsByScope(String scope, List<Metric> metrics) {
this.scope = scope;
this.metrics = metrics;
}
public MetricsByScope() {
this.scope = "";
this.metrics = new ArrayList<>();
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public List<Metric> getMetrics() {
return metrics;
}
public void setMetrics(List<Metric> metrics) {
this.metrics = metrics;
}
public void setMetrics(List<Metric> metrics) {
this.metrics = metrics;
}
/**
@ -95,14 +53,10 @@ public class EmittedMetrics {
private String description;
private String type;
private String unit;
private List<TelemetryAttribute> attributes;
private List<Attribute> attributes;
public Metric(
String name,
String description,
String type,
String unit,
List<TelemetryAttribute> attributes) {
String name, String description, String type, String unit, List<Attribute> attributes) {
this.name = name;
this.description = description;
this.type = type;
@ -150,12 +104,47 @@ public class EmittedMetrics {
this.unit = unit;
}
public List<TelemetryAttribute> getAttributes() {
public List<Attribute> getAttributes() {
return attributes;
}
public void setAttributes(List<TelemetryAttribute> attributes) {
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
}
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public static class Attribute {
private String name;
private String type;
public Attribute() {
this.name = "";
this.type = "";
}
public Attribute(String name, String type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}

View File

@ -1,127 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.internal;
import static java.util.Collections.emptyList;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
/**
* Representation of spans emitted by an instrumentation. Includes context about whether emitted by
* default or via a configuration option. This class is internal and is hence not for public use.
* Its APIs are unstable and can change at any time.
*/
public class EmittedSpans {
// Condition in which the telemetry is emitted (ex: default, or configuration option names).
private String when;
@JsonProperty("spans_by_scope")
private List<SpansByScope> spansByScope;
public EmittedSpans() {
this.when = "";
this.spansByScope = emptyList();
}
public EmittedSpans(String when, List<SpansByScope> spansByScope) {
this.when = when;
this.spansByScope = spansByScope;
}
public String getWhen() {
return when;
}
public void setWhen(String when) {
this.when = when;
}
@JsonProperty("spans_by_scope")
public List<SpansByScope> getSpansByScope() {
return spansByScope;
}
@JsonProperty("spans_by_scope")
public void setSpansByScope(List<SpansByScope> spans) {
this.spansByScope = spans;
}
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public static class SpansByScope {
private String scope;
private List<Span> spans;
public SpansByScope(String scopeName, List<Span> spans) {
this.scope = scopeName;
this.spans = spans;
}
public SpansByScope() {
this.scope = "";
this.spans = emptyList();
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public List<Span> getSpans() {
return spans;
}
public void setSpans(List<Span> spans) {
this.spans = spans;
}
}
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public static class Span {
@JsonProperty("span_kind")
private String spanKind;
private List<TelemetryAttribute> attributes;
public Span(String spanKind, List<TelemetryAttribute> attributes) {
this.spanKind = spanKind;
this.attributes = attributes;
}
public Span() {
this.spanKind = "";
this.attributes = new ArrayList<>();
}
@JsonProperty("span_kind")
public String getSpanKind() {
return spanKind;
}
@JsonProperty("span_kind")
public void setSpanKind(String spanKind) {
this.spanKind = spanKind;
}
public List<TelemetryAttribute> getAttributes() {
return attributes;
}
public void setAttributes(List<TelemetryAttribute> attributes) {
this.attributes = attributes;
}
}
}

View File

@ -29,7 +29,6 @@ public class InstrumentationModule {
private final String group;
private final InstrumentationScopeInfo scopeInfo;
private Map<String, List<EmittedMetrics.Metric>> metrics;
private Map<String, List<EmittedSpans.Span>> spans;
@Nullable private Map<InstrumentationType, Set<String>> targetVersions;
@ -48,7 +47,6 @@ public class InstrumentationModule {
requireNonNull(builder.group, "group required");
this.metrics = Objects.requireNonNullElseGet(builder.metrics, HashMap::new);
this.spans = Objects.requireNonNullElseGet(builder.spans, HashMap::new);
this.srcPath = builder.srcPath;
this.instrumentationName = builder.instrumentationName;
this.namespace = builder.namespace;
@ -101,10 +99,6 @@ public class InstrumentationModule {
return metrics;
}
public Map<String, List<EmittedSpans.Span>> getSpans() {
return spans;
}
public void setTargetVersions(Map<InstrumentationType, Set<String>> targetVersions) {
this.targetVersions = targetVersions;
}
@ -121,10 +115,6 @@ public class InstrumentationModule {
this.metrics = metrics;
}
public void setSpans(Map<String, List<EmittedSpans.Span>> spans) {
this.spans = spans;
}
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
@ -138,7 +128,6 @@ public class InstrumentationModule {
@Nullable private InstrumentationMetaData metadata;
@Nullable private Map<InstrumentationType, Set<String>> targetVersions;
@Nullable private Map<String, List<EmittedMetrics.Metric>> metrics;
@Nullable private Map<String, List<EmittedSpans.Span>> spans;
@CanIgnoreReturnValue
public Builder srcPath(String srcPath) {
@ -188,12 +177,6 @@ public class InstrumentationModule {
return this;
}
@CanIgnoreReturnValue
public Builder spans(Map<String, List<EmittedSpans.Span>> spans) {
this.spans = spans;
return this;
}
public InstrumentationModule build() {
return new InstrumentationModule(this);
}

View File

@ -1,61 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.internal;
import java.util.Objects;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public class TelemetryAttribute {
private String name;
private String type;
public TelemetryAttribute() {
this.name = "";
this.type = "";
}
public TelemetryAttribute(String name, String type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
// Overriding equals and hashCode to compare TelemetryAttribute objects based on name and type
// This is important for our deduplication logic in the documentation generation process.
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TelemetryAttribute that)) {
return false;
}
return Objects.equals(name, that.name) && Objects.equals(type, that.type);
}
@Override
public int hashCode() {
return Objects.hash(name, type);
}
}

View File

@ -1,169 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.parsers;
import com.fasterxml.jackson.core.JsonProcessingException;
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Stream;
/**
* This class is responsible for parsing metric-* files from the `.telemetry` directory of an
* instrumentation module and converting them into the {@link EmittedMetrics} format.
*/
public class EmittedMetricsParser {
private static final Logger logger = Logger.getLogger(EmittedMetricsParser.class.getName());
/**
* Looks for metric files in the .telemetry directory, and combines them into a map where the key
* represents the "when", and the value is a list of metrics emitted under that condition.
*
* @param instrumentationDirectory the directory to traverse
* @return contents of aggregated files
*/
public static Map<String, EmittedMetrics> getMetricsFromFiles(
String rootDir, String instrumentationDirectory) {
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen =
parseAllMetricFiles(telemetryDir);
return aggregateMetricsByScope(metricsByWhen);
}
/**
* Parses all metric files in the given .telemetry directory and returns a map where the key is
* the 'when' condition and the value is a list of metrics grouped by scope.
*
* @param telemetryDir the path to the .telemetry directory
* @return a map of 'when' to list of metrics by scope
*/
private static Map<String, List<EmittedMetrics.MetricsByScope>> parseAllMetricFiles(
Path telemetryDir) {
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen = new HashMap<>();
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
try (Stream<Path> files = Files.list(telemetryDir)) {
files
.filter(path -> path.getFileName().toString().startsWith("metrics-"))
.forEach(
path -> {
String content = FileManager.readFileToString(path.toString());
if (content != null) {
String when = content.substring(0, content.indexOf('\n'));
String whenKey = when.replace("when: ", "");
int metricsIndex = content.indexOf("metrics_by_scope:");
if (metricsIndex != -1) {
String yaml = "when: " + whenKey + "\n" + content.substring(metricsIndex);
EmittedMetrics parsed;
try {
parsed = YamlHelper.emittedMetricsParser(yaml);
} catch (Exception e) {
logger.severe(
"Error parsing metrics file (" + path + "): " + e.getMessage());
return;
}
if (parsed.getMetricsByScope() != null) {
metricsByWhen.putIfAbsent(whenKey, new ArrayList<>());
metricsByWhen.get(whenKey).addAll(parsed.getMetricsByScope());
}
}
}
});
} catch (IOException e) {
logger.severe("Error reading metrics files: " + e.getMessage());
}
}
return metricsByWhen;
}
/**
* Aggregates metrics under the same scope for each 'when' condition, deduplicating metrics by
* name.
*
* @param metricsByWhen map of 'when' to list of metrics by scope
* @return a map of 'when' to aggregated EmittedMetrics
*/
private static Map<String, EmittedMetrics> aggregateMetricsByScope(
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen) {
Map<String, EmittedMetrics> result = new HashMap<>();
for (Map.Entry<String, List<EmittedMetrics.MetricsByScope>> entry : metricsByWhen.entrySet()) {
String when = entry.getKey();
List<EmittedMetrics.MetricsByScope> allScopes = entry.getValue();
Map<String, Map<String, EmittedMetrics.Metric>> metricsByScopeName = new HashMap<>();
for (EmittedMetrics.MetricsByScope scopeEntry : allScopes) {
String scope = scopeEntry.getScope();
metricsByScopeName.putIfAbsent(scope, new HashMap<>());
Map<String, EmittedMetrics.Metric> metricMap = metricsByScopeName.get(scope);
for (EmittedMetrics.Metric metric : scopeEntry.getMetrics()) {
metricMap.put(metric.getName(), metric); // deduplicate by name
}
}
List<EmittedMetrics.MetricsByScope> mergedScopes = new ArrayList<>();
for (Map.Entry<String, Map<String, EmittedMetrics.Metric>> scopeEntry :
metricsByScopeName.entrySet()) {
mergedScopes.add(
new EmittedMetrics.MetricsByScope(
scopeEntry.getKey(), new ArrayList<>(scopeEntry.getValue().values())));
}
result.put(when, new EmittedMetrics(when, mergedScopes));
}
return result;
}
/**
* Takes in a raw string representation of the aggregated EmittedMetrics yaml map, separated by
* the {@code when}, indicating the conditions under which the metrics are emitted. Deduplicates
* the metrics by name and then returns a new map of EmittedMetrics objects.
*
* @param input raw string representation of EmittedMetrics yaml
* @return map where the key is the {@code when} condition and the value is the corresponding
* EmittedMetrics
* @throws JsonProcessingException if parsing fails
*/
// visible for testing
public static Map<String, EmittedMetrics> parseMetrics(Map<String, StringBuilder> input)
throws JsonProcessingException {
Map<String, EmittedMetrics> metricsMap = new HashMap<>();
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
String when = entry.getKey();
StringBuilder content = entry.getValue();
EmittedMetrics metrics = YamlHelper.emittedMetricsParser(content.toString());
if (metrics.getMetricsByScope() == null) {
continue;
}
List<EmittedMetrics.MetricsByScope> deduplicatedScopes = new ArrayList<>();
for (EmittedMetrics.MetricsByScope scopeEntry : metrics.getMetricsByScope()) {
String scope = scopeEntry.getScope();
Map<String, EmittedMetrics.Metric> dedupedMetrics = new HashMap<>();
for (EmittedMetrics.Metric metric : scopeEntry.getMetrics()) {
dedupedMetrics.put(metric.getName(), metric);
}
deduplicatedScopes.add(
new EmittedMetrics.MetricsByScope(scope, new ArrayList<>(dedupedMetrics.values())));
}
metricsMap.put(when, new EmittedMetrics(when, deduplicatedScopes));
}
return metricsMap;
}
private EmittedMetricsParser() {}
}

View File

@ -1,175 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.parsers;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Stream;
/**
* This class is responsible for parsing span-* files from the `.telemetry` directory of an
* instrumentation module and converting them into the {@link EmittedSpans} format.
*/
public class EmittedSpanParser {
private static final Logger logger = Logger.getLogger(EmittedSpanParser.class.getName());
/**
* Looks for span files in the .telemetry directory, and combines them into a single map.
*
* @param instrumentationDirectory the directory to traverse
* @return contents of aggregated files
*/
public static Map<String, EmittedSpans> getSpansByScopeFromFiles(
String rootDir, String instrumentationDirectory) throws JsonProcessingException {
Map<String, StringBuilder> spansByScope = new HashMap<>();
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
try (Stream<Path> files = Files.list(telemetryDir)) {
files
.filter(path -> path.getFileName().toString().startsWith("spans-"))
.forEach(
path -> {
String content = FileManager.readFileToString(path.toString());
if (content != null) {
String when = content.substring(0, content.indexOf('\n'));
String whenKey = when.replace("when: ", "");
spansByScope.putIfAbsent(whenKey, new StringBuilder("spans_by_scope:\n"));
// Skip the spans_by_scope line so we can aggregate into one list
int spanIndex = content.indexOf("spans_by_scope:\n");
if (spanIndex != -1) {
String contentAfter =
content.substring(spanIndex + "spans_by_scope:\n".length());
spansByScope.get(whenKey).append(contentAfter);
}
}
});
} catch (IOException e) {
logger.severe("Error reading span files: " + e.getMessage());
}
}
return parseSpans(spansByScope);
}
/**
* Takes in a raw string representation of the aggregated EmittedSpan yaml map, separated by the
* `when`, indicating the conditions under which the telemetry is emitted. deduplicates by name
* and then returns a new map.
*
* @param input raw string representation of EmittedSpans yaml
* @return {@code Map<String, EmittedSpans>} where the key is the `when` condition
*/
private static Map<String, EmittedSpans> parseSpans(Map<String, StringBuilder> input)
throws JsonProcessingException {
Map<String, EmittedSpans> result = new HashMap<>();
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
String when = entry.getKey().strip();
StringBuilder content = entry.getValue();
EmittedSpans spans = YamlHelper.emittedSpansParser(content.toString());
if (spans.getSpansByScope().isEmpty()) {
continue;
}
Map<String, Map<String, Set<TelemetryAttribute>>> attributesByScopeAndSpanKind =
new HashMap<>();
for (EmittedSpans.SpansByScope spansByScopeEntry : spans.getSpansByScope()) {
String scope = spansByScopeEntry.getScope();
attributesByScopeAndSpanKind.putIfAbsent(scope, new HashMap<>());
Map<String, Set<TelemetryAttribute>> attributesBySpanKind =
attributesByScopeAndSpanKind.get(scope);
for (EmittedSpans.Span span : spansByScopeEntry.getSpans()) {
String spanKind = span.getSpanKind();
attributesBySpanKind.putIfAbsent(spanKind, new HashSet<>());
Set<TelemetryAttribute> attributeSet = attributesBySpanKind.get(spanKind);
if (span.getAttributes() != null) {
for (TelemetryAttribute attr : span.getAttributes()) {
attributeSet.add(new TelemetryAttribute(attr.getName(), attr.getType()));
}
}
}
}
EmittedSpans deduplicatedEmittedSpans = getEmittedSpans(attributesByScopeAndSpanKind, when);
result.put(when, deduplicatedEmittedSpans);
}
return result;
}
/**
* Takes in a map of attributes by scope and span kind, and returns an {@link EmittedSpans} object
* with deduplicated spans.
*
* @param attributesByScopeAndSpanKind the map of attributes by scope and span kind
* @param when the condition under which the telemetry is emitted
* @return an {@link EmittedSpans} object with deduplicated spans
*/
private static EmittedSpans getEmittedSpans(
Map<String, Map<String, Set<TelemetryAttribute>>> attributesByScopeAndSpanKind, String when) {
List<EmittedSpans.SpansByScope> deduplicatedSpansByScope = new ArrayList<>();
for (Map.Entry<String, Map<String, Set<TelemetryAttribute>>> scopeEntry :
attributesByScopeAndSpanKind.entrySet()) {
String scope = scopeEntry.getKey();
Map<String, Set<TelemetryAttribute>> spanKindMap = scopeEntry.getValue();
EmittedSpans.SpansByScope deduplicatedScope = getSpansByScope(scope, spanKindMap);
deduplicatedSpansByScope.add(deduplicatedScope);
}
return new EmittedSpans(when, deduplicatedSpansByScope);
}
/**
* Converts a map of attributes by spanKind into an {@link EmittedSpans.SpansByScope} object.
* Deduplicates spans by their kind and collects their attributes.
*
* @param scope the name of the scope
* @param spanKindMap a map where the key is the span kind and the value is set of attributes
* @return an {@link EmittedSpans.SpansByScope} object with deduplicated spans
*/
private static EmittedSpans.SpansByScope getSpansByScope(
String scope, Map<String, Set<TelemetryAttribute>> spanKindMap) {
List<EmittedSpans.Span> deduplicatedSpans = new ArrayList<>();
for (Map.Entry<String, Set<TelemetryAttribute>> spanKindEntry : spanKindMap.entrySet()) {
String spanKind = spanKindEntry.getKey();
Set<TelemetryAttribute> attributes = spanKindEntry.getValue();
List<TelemetryAttribute> attributeList = new ArrayList<>(attributes);
EmittedSpans.Span deduplicatedSpan = new EmittedSpans.Span(spanKind, attributeList);
deduplicatedSpans.add(deduplicatedSpan);
}
return new EmittedSpans.SpansByScope(scope, deduplicatedSpans);
}
private EmittedSpanParser() {}
}

View File

@ -6,21 +6,17 @@
package io.opentelemetry.instrumentation.docs.parsers;
import io.opentelemetry.instrumentation.docs.internal.DependencyInfo;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import io.opentelemetry.instrumentation.docs.utils.FileManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/** Handles parsing of Gradle build files to extract muzzle and dependency information. */
public class GradleParser {
private static final Pattern variablePattern =
@ -245,58 +241,5 @@ public class GradleParser {
return null;
}
public static Map<InstrumentationType, Set<String>> extractVersions(
List<String> gradleFiles, InstrumentationModule module) {
Map<InstrumentationType, Set<String>> versionsByType = new HashMap<>();
gradleFiles.forEach(file -> processGradleFile(file, versionsByType, module));
return versionsByType;
}
private static void processGradleFile(
String filePath,
Map<InstrumentationType, Set<String>> versionsByType,
InstrumentationModule module) {
String fileContents = FileManager.readFileToString(filePath);
if (fileContents == null) {
return;
}
Optional<InstrumentationType> type = determineInstrumentationType(filePath);
if (type.isEmpty()) {
return;
}
DependencyInfo dependencyInfo = parseGradleFile(fileContents, type.get());
if (dependencyInfo == null) {
return;
}
addVersions(versionsByType, type.get(), dependencyInfo.versions());
setMinJavaVersionIfPresent(module, dependencyInfo);
}
private static Optional<InstrumentationType> determineInstrumentationType(String filePath) {
if (filePath.contains("/javaagent/")) {
return Optional.of(InstrumentationType.JAVAAGENT);
} else if (filePath.contains("/library/")) {
return Optional.of(InstrumentationType.LIBRARY);
}
return Optional.empty();
}
private static void addVersions(
Map<InstrumentationType, Set<String>> versionsByType,
InstrumentationType type,
Set<String> versions) {
versionsByType.computeIfAbsent(type, k -> new HashSet<>()).addAll(versions);
}
private static void setMinJavaVersionIfPresent(
InstrumentationModule module, DependencyInfo dependencyInfo) {
if (dependencyInfo.minJavaVersionSupported() != null) {
module.setMinJavaVersion(dependencyInfo.minJavaVersionSupported());
}
}
private GradleParser() {}
}

View File

@ -6,174 +6,93 @@
package io.opentelemetry.instrumentation.docs.parsers;
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Stream;
/**
* This class is responsible for parsing metric files from the `.telemetry` directory of an
* instrumentation module and filtering them by scope.
*/
public class MetricParser {
private static final Logger logger = Logger.getLogger(MetricParser.class.getName());
/**
* Retrieves metrics for a given instrumentation module, filtered by scope.
* Looks for metric files in the .telemetry directory, and combines them into a single list of
* metrics.
*
* @param module the instrumentation module
* @param fileManager the file manager to use for file operations
* @return a map where the key is the 'when' condition and the value is a list of metrics
* @param instrumentationDirectory the directory to traverse
* @return contents of aggregated files
*/
public static Map<String, List<EmittedMetrics.Metric>> getMetrics(
InstrumentationModule module, FileManager fileManager) {
Map<String, EmittedMetrics> metrics =
EmittedMetricsParser.getMetricsFromFiles(fileManager.rootDir(), module.getSrcPath());
public static Map<String, EmittedMetrics> getMetricsFromFiles(
String rootDir, String instrumentationDirectory) {
Map<String, StringBuilder> metricsByWhen = new HashMap<>();
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
if (metrics.isEmpty()) {
return new HashMap<>();
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
try (Stream<Path> files = Files.list(telemetryDir)) {
files
.filter(path -> path.getFileName().toString().startsWith("metrics-"))
.forEach(
path -> {
String content = FileManager.readFileToString(path.toString());
if (content != null) {
String when = content.substring(0, content.indexOf('\n'));
String whenKey = when.replace("when: ", "");
metricsByWhen.putIfAbsent(whenKey, new StringBuilder("metrics:\n"));
// Skip the metric label ("metrics:") so we can aggregate into one list
int metricsIndex = content.indexOf("metrics:\n");
if (metricsIndex != -1) {
String contentAfterMetrics =
content.substring(metricsIndex + "metrics:\n".length());
metricsByWhen.get(whenKey).append(contentAfterMetrics);
}
}
});
} catch (IOException e) {
logger.severe("Error reading metrics files: " + e.getMessage());
}
}
String scopeName = module.getScopeInfo().getName();
return filterMetricsByScope(metrics, scopeName);
return parseMetrics(metricsByWhen);
}
/**
* Filters metrics by scope and aggregates attributes for each metric kind.
* Takes in a raw string representation of the aggregated EmittedMetrics yaml map, separated by
* the `when`, indicating the conditions under which the metrics are emitted. deduplicates the
* metrics by name and then returns a new map EmittedMetrics objects.
*
* @param metricsByScope the map of metrics by scope
* @param scopeName the name of the scope to filter metrics for
* @return a map of filtered metrics by 'when'
* @param input raw string representation of EmittedMetrics yaml
* @return {@code Map<String, EmittedMetrics>} where the key is the `when` condition
*/
private static Map<String, List<EmittedMetrics.Metric>> filterMetricsByScope(
Map<String, EmittedMetrics> metricsByScope, String scopeName) {
// visible for testing
public static Map<String, EmittedMetrics> parseMetrics(Map<String, StringBuilder> input) {
Map<String, EmittedMetrics> metricsMap = new HashMap<>();
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
String when = entry.getKey();
StringBuilder content = entry.getValue();
Map<String, Map<String, MetricAggregator.AggregatedMetricInfo>> aggregatedMetrics =
new HashMap<>();
for (Map.Entry<String, EmittedMetrics> entry : metricsByScope.entrySet()) {
if (!hasValidMetrics(entry.getValue())) {
EmittedMetrics metrics = YamlHelper.emittedMetricsParser(content.toString());
if (metrics.getMetrics() == null) {
continue;
}
String when = entry.getValue().getWhen();
Map<String, Map<String, MetricAggregator.AggregatedMetricInfo>> result =
MetricAggregator.aggregateMetrics(when, entry.getValue(), scopeName);
// Merge result into aggregatedMetrics
for (Map.Entry<String, Map<String, MetricAggregator.AggregatedMetricInfo>> e :
result.entrySet()) {
String whenKey = e.getKey();
Map<String, MetricAggregator.AggregatedMetricInfo> metricMap =
aggregatedMetrics.computeIfAbsent(whenKey, k -> new HashMap<>());
for (Map.Entry<String, MetricAggregator.AggregatedMetricInfo> metricEntry :
e.getValue().entrySet()) {
String metricName = metricEntry.getKey();
MetricAggregator.AggregatedMetricInfo newInfo = metricEntry.getValue();
MetricAggregator.AggregatedMetricInfo existingInfo = metricMap.get(metricName);
if (existingInfo == null) {
metricMap.put(metricName, newInfo);
} else {
existingInfo.attributes.addAll(newInfo.attributes);
}
}
Map<String, EmittedMetrics.Metric> deduplicatedMetrics = new HashMap<>();
for (EmittedMetrics.Metric metric : metrics.getMetrics()) {
deduplicatedMetrics.put(metric.getName(), metric);
}
List<EmittedMetrics.Metric> uniqueMetrics = new ArrayList<>(deduplicatedMetrics.values());
metricsMap.put(when, new EmittedMetrics(when, uniqueMetrics));
}
return MetricAggregator.buildFilteredMetrics(aggregatedMetrics);
}
private static boolean hasValidMetrics(EmittedMetrics metrics) {
return metrics != null && metrics.getMetricsByScope() != null;
}
/** Helper class to aggregate metrics by scope and name. */
static class MetricAggregator {
/**
* Aggregates metrics for a given 'when' condition, metrics object, and target scope name.
*
* @param when the 'when' condition
* @param metrics the EmittedMetrics object
* @param targetScopeName the scope name to filter by
* @return a map of aggregated metrics by 'when' and metric name
*/
public static Map<String, Map<String, AggregatedMetricInfo>> aggregateMetrics(
String when, EmittedMetrics metrics, String targetScopeName) {
Map<String, Map<String, AggregatedMetricInfo>> aggregatedMetrics = new HashMap<>();
Map<String, AggregatedMetricInfo> metricKindMap =
aggregatedMetrics.computeIfAbsent(when, k -> new HashMap<>());
for (EmittedMetrics.MetricsByScope metricsByScope : metrics.getMetricsByScope()) {
if (metricsByScope.getScope().equals(targetScopeName)) {
for (EmittedMetrics.Metric metric : metricsByScope.getMetrics()) {
AggregatedMetricInfo aggInfo =
metricKindMap.computeIfAbsent(
metric.getName(),
k ->
new AggregatedMetricInfo(
metric.getName(),
metric.getDescription(),
metric.getType(),
metric.getUnit()));
if (metric.getAttributes() != null) {
for (TelemetryAttribute attr : metric.getAttributes()) {
aggInfo.attributes.add(new TelemetryAttribute(attr.getName(), attr.getType()));
}
}
}
}
}
return aggregatedMetrics;
}
/**
* Builds a filtered metrics map from aggregated metrics.
*
* @param aggregatedMetrics the aggregated metrics map
* @return a map where the key is the 'when' condition and the value is a list of metrics
*/
public static Map<String, List<EmittedMetrics.Metric>> buildFilteredMetrics(
Map<String, Map<String, AggregatedMetricInfo>> aggregatedMetrics) {
Map<String, List<EmittedMetrics.Metric>> result = new HashMap<>();
for (Map.Entry<String, Map<String, AggregatedMetricInfo>> entry :
aggregatedMetrics.entrySet()) {
String when = entry.getKey();
List<EmittedMetrics.Metric> metrics = result.computeIfAbsent(when, k -> new ArrayList<>());
for (AggregatedMetricInfo aggInfo : entry.getValue().values()) {
metrics.add(
new EmittedMetrics.Metric(
aggInfo.name,
aggInfo.description,
aggInfo.type,
aggInfo.unit,
new ArrayList<>(aggInfo.attributes)));
}
}
return result;
}
/** Data class to hold aggregated metric information. */
static class AggregatedMetricInfo {
final String name;
final String description;
final String type;
final String unit;
final Set<TelemetryAttribute> attributes = new HashSet<>();
AggregatedMetricInfo(String name, String description, String type, String unit) {
this.name = name;
this.description = description;
this.type = type;
this.unit = unit;
}
}
private MetricAggregator() {}
return metricsMap;
}
private MetricParser() {}

View File

@ -1,56 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.parsers;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Handles conversion of InstrumentationPath objects to InstrumentationModule objects. */
public class ModuleParser {
/**
* Converts a list of {@link InstrumentationPath} into a list of {@link InstrumentationModule}.
*
* @param rootPath the root path for sanitization
* @param paths the list of {@link InstrumentationPath} objects to be converted
* @return a list of {@link InstrumentationModule} objects
*/
public static List<InstrumentationModule> convertToModules(
String rootPath, List<InstrumentationPath> paths) {
Map<String, InstrumentationModule> moduleMap = new HashMap<>();
for (InstrumentationPath path : paths) {
String moduleKey = createModuleKey(path);
moduleMap.computeIfAbsent(moduleKey, k -> createModule(rootPath, path));
}
return new ArrayList<>(moduleMap.values());
}
private static String createModuleKey(InstrumentationPath path) {
return String.join(":", path.group(), path.namespace(), path.instrumentationName());
}
private static InstrumentationModule createModule(String rootPath, InstrumentationPath path) {
return new InstrumentationModule.Builder()
.srcPath(sanitizePathName(rootPath, path.srcPath()))
.instrumentationName(path.instrumentationName())
.namespace(path.namespace())
.group(path.group())
.build();
}
// visible for testing
public static String sanitizePathName(String rootPath, String path) {
return path.replace(rootPath, "").replace("/javaagent", "").replace("/library", "");
}
private ModuleParser() {}
}

View File

@ -1,143 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.parsers;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
import io.opentelemetry.instrumentation.docs.utils.FileManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class is responsible for parsing span files from the `.telemetry` directory of an
* instrumentation module and filtering them by scope.
*/
public class SpanParser {
// We want to ignore test related attributes
private static final List<String> EXCLUDED_ATTRIBUTES = List.of("x-test-", "test-baggage-");
/**
* Pull spans from the `.telemetry` directory, filter them by scope, and set them in the module.
*
* @param module the instrumentation module to extract spans for
* @param fileManager the file manager to access the filesystem
* @throws JsonProcessingException if there is an error processing the JSON from the span files
*/
public static Map<String, List<EmittedSpans.Span>> getSpans(
InstrumentationModule module, FileManager fileManager) throws JsonProcessingException {
Map<String, EmittedSpans> spans =
EmittedSpanParser.getSpansByScopeFromFiles(fileManager.rootDir(), module.getSrcPath());
if (spans.isEmpty()) {
return new HashMap<>();
}
String scopeName = module.getScopeInfo().getName();
return filterSpansByScope(spans, scopeName);
}
/**
* Filters spans by scope and aggregates attributes for each span kind.
*
* @param spansByScope the map of spans by scope
* @param scopeName the name of the scope to filter spans for
* @return a map of filtered spans by `when`
*/
private static Map<String, List<EmittedSpans.Span>> filterSpansByScope(
Map<String, EmittedSpans> spansByScope, String scopeName) {
Map<String, Map<String, Set<TelemetryAttribute>>> aggregatedAttributes = new HashMap<>();
for (Map.Entry<String, EmittedSpans> entry : spansByScope.entrySet()) {
if (!hasValidSpans(entry.getValue())) {
continue;
}
String when = entry.getValue().getWhen();
Map<String, Map<String, Set<TelemetryAttribute>>> result =
SpanAggregator.aggregateSpans(when, entry.getValue(), scopeName);
aggregatedAttributes.putAll(result);
}
return SpanAggregator.buildFilteredSpans(aggregatedAttributes);
}
private static boolean hasValidSpans(EmittedSpans spans) {
return spans != null && spans.getSpansByScope() != null;
}
/** Helper class to aggregate span attributes by scope and kind. */
static class SpanAggregator {
public static Map<String, Map<String, Set<TelemetryAttribute>>> aggregateSpans(
String when, EmittedSpans spans, String targetScopeName) {
Map<String, Map<String, Set<TelemetryAttribute>>> aggregatedAttributes = new HashMap<>();
Map<String, Set<TelemetryAttribute>> spanKindMap =
aggregatedAttributes.computeIfAbsent(when, k -> new HashMap<>());
for (EmittedSpans.SpansByScope spansByScope : spans.getSpansByScope()) {
if (spansByScope.getScope().equals(targetScopeName)) {
processSpansForScope(spansByScope, spanKindMap);
}
}
return aggregatedAttributes;
}
private static void processSpansForScope(
EmittedSpans.SpansByScope spansByScope, Map<String, Set<TelemetryAttribute>> spanKindMap) {
for (EmittedSpans.Span span : spansByScope.getSpans()) {
Set<TelemetryAttribute> attributes =
spanKindMap.computeIfAbsent(span.getSpanKind(), k -> new HashSet<>());
addSpanAttributes(span, attributes);
}
}
private static void addSpanAttributes(
EmittedSpans.Span span, Set<TelemetryAttribute> attributes) {
if (span.getAttributes() == null) {
return;
}
for (TelemetryAttribute attr : span.getAttributes()) {
boolean excluded = EXCLUDED_ATTRIBUTES.stream().anyMatch(ex -> attr.getName().contains(ex));
if (!excluded) {
attributes.add(new TelemetryAttribute(attr.getName(), attr.getType()));
}
}
}
public static Map<String, List<EmittedSpans.Span>> buildFilteredSpans(
Map<String, Map<String, Set<TelemetryAttribute>>> aggregatedAttributes) {
Map<String, List<EmittedSpans.Span>> result = new HashMap<>();
for (Map.Entry<String, Map<String, Set<TelemetryAttribute>>> entry :
aggregatedAttributes.entrySet()) {
String when = entry.getKey();
List<EmittedSpans.Span> spans = result.computeIfAbsent(when, k -> new ArrayList<>());
for (Map.Entry<String, Set<TelemetryAttribute>> kindEntry : entry.getValue().entrySet()) {
String spanKind = kindEntry.getKey();
Set<TelemetryAttribute> attributes = kindEntry.getValue();
spans.add(new EmittedSpans.Span(spanKind, new ArrayList<>(attributes)));
}
}
return result;
}
private SpanAggregator() {}
}
private SpanParser() {}
}

View File

@ -11,11 +11,9 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.opentelemetry.instrumentation.docs.internal.ConfigurationOption;
import io.opentelemetry.instrumentation.docs.internal.ConfigurationType;
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
import java.io.BufferedWriter;
import java.util.ArrayList;
import java.util.Collections;
@ -77,16 +75,7 @@ public class YamlHelper {
.equals(InstrumentationClassification.LIBRARY))
.collect(
Collectors.groupingBy(
InstrumentationModule::getGroup,
TreeMap::new,
Collectors.collectingAndThen(
Collectors.toList(),
modules ->
modules.stream()
.sorted(
Comparator.comparing(
InstrumentationModule::getInstrumentationName))
.collect(Collectors.toList()))));
InstrumentationModule::getGroup, TreeMap::new, Collectors.toList()));
Map<String, Object> output = new TreeMap<>();
libraryInstrumentations.forEach(
@ -142,9 +131,8 @@ public class YamlHelper {
addTargetVersions(module, moduleMap);
addConfigurations(module, moduleMap);
// Get telemetry grouping lists
Set<String> telemetryGroups = new java.util.HashSet<>(module.getMetrics().keySet());
telemetryGroups.addAll(module.getSpans().keySet());
// Get telemetry grouping list
Set<String> telemetryGroups = module.getMetrics().keySet();
if (!telemetryGroups.isEmpty()) {
List<Map<String, Object>> telemetryList = new ArrayList<>();
@ -155,38 +143,16 @@ public class YamlHelper {
new ArrayList<>(module.getMetrics().getOrDefault(group, Collections.emptyList()));
List<Map<String, Object>> metricsList = new ArrayList<>();
// sort by name for determinism in the order
// sort metrics by name for some determinism in the order
metrics.sort(Comparator.comparing(EmittedMetrics.Metric::getName));
for (EmittedMetrics.Metric metric : metrics) {
metricsList.add(getMetricsMap(metric));
}
if (!metricsList.isEmpty()) {
telemetryEntry.put("metrics", metricsList);
}
List<EmittedSpans.Span> spans =
new ArrayList<>(module.getSpans().getOrDefault(group, Collections.emptyList()));
List<Map<String, Object>> spanList = new ArrayList<>();
// sort by name for determinism in the order
spans.sort(Comparator.comparing(EmittedSpans.Span::getSpanKind));
for (EmittedSpans.Span span : spans) {
spanList.add(getSpanMap(span));
}
if (!spanList.isEmpty()) {
telemetryEntry.put("spans", spanList);
}
if (!spanList.isEmpty() || !metricsList.isEmpty()) {
telemetryList.add(telemetryEntry);
}
}
if (!telemetryList.isEmpty()) {
moduleMap.put("telemetry", telemetryList);
telemetryEntry.put("metrics", metricsList);
telemetryList.add(telemetryEntry);
}
moduleMap.put("telemetry", telemetryList);
}
return moduleMap;
}
@ -255,34 +221,21 @@ public class YamlHelper {
return conf;
}
private static List<Map<String, Object>> getSortedAttributeMaps(
List<TelemetryAttribute> attributes) {
List<TelemetryAttribute> sortedAttributes = new ArrayList<>(attributes);
sortedAttributes.sort(Comparator.comparing(TelemetryAttribute::getName));
List<Map<String, Object>> attributeMaps = new ArrayList<>();
for (TelemetryAttribute attribute : sortedAttributes) {
Map<String, Object> attributeMap = new LinkedHashMap<>();
attributeMap.put("name", attribute.getName());
attributeMap.put("type", attribute.getType());
attributeMaps.add(attributeMap);
}
return attributeMaps;
}
private static Map<String, Object> getMetricsMap(EmittedMetrics.Metric metric) {
Map<String, Object> innerMetricMap = new LinkedHashMap<>();
innerMetricMap.put("name", metric.getName());
innerMetricMap.put("description", metric.getDescription());
innerMetricMap.put("type", metric.getType());
innerMetricMap.put("unit", metric.getUnit());
innerMetricMap.put("attributes", getSortedAttributeMaps(metric.getAttributes()));
return innerMetricMap;
}
private static Map<String, Object> getSpanMap(EmittedSpans.Span span) {
Map<String, Object> innerMetricMap = new LinkedHashMap<>();
innerMetricMap.put("span_kind", span.getSpanKind());
innerMetricMap.put("attributes", getSortedAttributeMaps(span.getAttributes()));
List<Map<String, Object>> attributes = new ArrayList<>();
for (EmittedMetrics.Attribute attribute : metric.getAttributes()) {
Map<String, Object> attributeMap = new LinkedHashMap<>();
attributeMap.put("name", attribute.getName());
attributeMap.put("type", attribute.getType());
attributes.add(attributeMap);
}
innerMetricMap.put("attributes", attributes);
return innerMetricMap;
}
@ -291,12 +244,8 @@ public class YamlHelper {
return mapper.readValue(input, InstrumentationMetaData.class);
}
public static EmittedMetrics emittedMetricsParser(String input) throws JsonProcessingException {
return mapper.readValue(input, EmittedMetrics.class);
}
public static EmittedSpans emittedSpansParser(String input) throws JsonProcessingException {
return mapper.readValue(input, EmittedSpans.class);
public static EmittedMetrics emittedMetricsParser(String input) {
return new Yaml().loadAs(input, EmittedMetrics.class);
}
private YamlHelper() {}

View File

@ -9,7 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
import java.util.Arrays;
import java.util.List;
@ -41,7 +40,8 @@ class InstrumentationAnalyzerTest {
"spring",
InstrumentationType.LIBRARY));
List<InstrumentationModule> modules = ModuleParser.convertToModules("test", paths);
List<InstrumentationModule> modules =
InstrumentationAnalyzer.convertToInstrumentationModules("test", paths);
assertThat(modules.size()).isEqualTo(2);
@ -70,38 +70,4 @@ class InstrumentationAnalyzerTest {
assertThat(springModule.getSrcPath()).isEqualTo("instrumentation/spring/spring-web");
assertThat(springModule.getScopeInfo().getName()).isEqualTo("io.opentelemetry.spring-web");
}
@Test
void testModuleConverterCreatesUniqueModules() {
List<InstrumentationPath> paths =
Arrays.asList(
new InstrumentationPath(
"same-name",
"instrumentation/test1/same-name/library",
"namespace1",
"group1",
InstrumentationType.LIBRARY),
new InstrumentationPath(
"same-name",
"instrumentation/test2/same-name/library",
"namespace2",
"group2",
InstrumentationType.LIBRARY));
List<InstrumentationModule> modules = ModuleParser.convertToModules("test", paths);
// Should create 2 separate modules because they have different group/namespace combinations
assertThat(modules.size()).isEqualTo(2);
assertThat(
modules.stream()
.anyMatch(
m -> m.getGroup().equals("group1") && m.getNamespace().equals("namespace1")))
.isTrue();
assertThat(
modules.stream()
.anyMatch(
m -> m.getGroup().equals("group2") && m.getNamespace().equals("namespace2")))
.isTrue();
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
class ModuleConverterTest {
@Test
void testConvertToModulesCreatesUniqueModules() {
InstrumentationPath path1 = mock(InstrumentationPath.class);
when(path1.group()).thenReturn("g1");
when(path1.namespace()).thenReturn("n1");
when(path1.instrumentationName()).thenReturn("i1");
when(path1.srcPath()).thenReturn("/root/javaagent/foo");
InstrumentationPath path2 = mock(InstrumentationPath.class);
when(path2.group()).thenReturn("g1");
when(path2.namespace()).thenReturn("n1");
when(path2.instrumentationName()).thenReturn("i1");
when(path2.srcPath()).thenReturn("/root/library/bar");
InstrumentationPath path3 = mock(InstrumentationPath.class);
when(path3.group()).thenReturn("g2");
when(path3.namespace()).thenReturn("n2");
when(path3.instrumentationName()).thenReturn("i2");
when(path3.srcPath()).thenReturn("/root/javaagent/baz");
List<InstrumentationModule> modules =
ModuleParser.convertToModules("/root", Arrays.asList(path1, path2, path3));
assertThat(modules.size()).isEqualTo(2);
assertThat(modules)
.extracting(InstrumentationModule::getGroup)
.containsExactlyInAnyOrder("g1", "g2");
}
@Test
void testSanitizePathNameRemovesRootAndKnownFolders() throws Exception {
String sanitized = ModuleParser.sanitizePathName("/root", "/root/javaagent/foo/bar");
assertThat(sanitized).isEqualTo("/foo/bar");
sanitized = ModuleParser.sanitizePathName("/root", "/root/library/baz");
assertThat(sanitized).isEqualTo("/baz");
sanitized = ModuleParser.sanitizePathName("/root", "/root/other");
assertThat(sanitized).isEqualTo("/other");
}
}

View File

@ -1,128 +0,0 @@
/*
* 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 com.fasterxml.jackson.core.JsonProcessingException;
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.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.MockedStatic;
@SuppressWarnings("NullAway")
class EmittedMetricsParserTest {
@Test
void parseMetricsDeduplicatesMetricsByName() throws JsonProcessingException {
String input =
"""
metrics_by_scope:
- scope: io.opentelemetry.alibaba-druid-1.0
metrics:
- name: metric1
type: counter
- name: metric1
type: counter
- name: metric2
type: gauge
""";
Map<String, StringBuilder> metricMap = new HashMap<>();
metricMap.put("default", new StringBuilder(input));
Map<String, EmittedMetrics> result = EmittedMetricsParser.parseMetrics(metricMap);
List<String> metricNames =
result.get("default").getMetricsByScope().get(0).getMetrics().stream()
.map(EmittedMetrics.Metric::getName)
.sorted()
.toList();
assertThat(metricNames).hasSize(2);
assertThat(metricNames).containsExactly("metric1", "metric2");
}
@Test
void parseMetricsHandlesEmptyInput() throws JsonProcessingException {
String input = "metrics_by_scope:\n";
Map<String, StringBuilder> metricMap = new HashMap<>();
metricMap.put("default", new StringBuilder(input));
Map<String, EmittedMetrics> result = EmittedMetricsParser.parseMetrics(metricMap);
assertThat(result).isEmpty();
}
@Test
void getMetricsFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException {
Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry"));
String file1Content =
"""
when: default
metrics_by_scope:
- scope: io.opentelemetry.MetricParserTest
metrics:
- name: metric1
type: counter
""";
String file2Content =
"""
when: default
metrics_by_scope:
- scope: io.opentelemetry.MetricParserTest
metrics:
- name: metric2
type: gauge
""";
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<FileManager> 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);
Map<String, EmittedMetrics> result =
EmittedMetricsParser.getMetricsFromFiles(tempDir.toString(), "");
EmittedMetrics.MetricsByScope metrics =
result.get("default").getMetricsByScope().stream()
.filter(scope -> scope.getScope().equals("io.opentelemetry.MetricParserTest"))
.findFirst()
.orElseThrow();
assertThat(metrics.getMetrics()).hasSize(2);
List<String> metricNames =
metrics.getMetrics().stream().map(EmittedMetrics.Metric::getName).sorted().toList();
assertThat(metricNames).containsExactly("metric1", "metric2");
}
}
@Test
void getMetricsFromFilesHandlesNonexistentDirectory() throws JsonProcessingException {
Map<String, EmittedMetrics> result =
EmittedMetricsParser.getMetricsFromFiles("/nonexistent", "path");
assertThat(result).isEmpty();
}
}

View File

@ -6,110 +6,97 @@
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.internal.TelemetryAttribute;
import io.opentelemetry.instrumentation.docs.utils.FileManager;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.MockedStatic;
@SuppressWarnings("NullAway")
class MetricParserTest {
@Test
void testFiltersMetricsByScope() {
String targetScopeName = "my-instrumentation-scope";
void parseMetricsDeduplicatesMetricsByName() {
String input =
"""
metrics:
- name: metric1
type: counter
- name: metric1
type: counter
- name: metric2
type: gauge
""";
EmittedMetrics.Metric metric1 = createMetric("my.metric1", "desc1", "attr1");
EmittedMetrics.Metric otherMetric = createMetric("other.metric", "desc2", "other.attr");
Map<String, StringBuilder> metricMap = new HashMap<>();
metricMap.put("default", new StringBuilder(input));
EmittedMetrics.MetricsByScope targetMetricsByScope =
new EmittedMetrics.MetricsByScope(targetScopeName, List.of(metric1));
Map<String, EmittedMetrics> result = MetricParser.parseMetrics(metricMap);
List<String> metricNames =
result.get("default").getMetrics().stream()
.map(EmittedMetrics.Metric::getName)
.sorted()
.toList();
EmittedMetrics.MetricsByScope otherMetricsByScope =
new EmittedMetrics.MetricsByScope("other-scope", List.of(otherMetric));
EmittedMetrics emittedMetrics =
new EmittedMetrics("default", List.of(targetMetricsByScope, otherMetricsByScope));
Map<String, Map<String, MetricParser.MetricAggregator.AggregatedMetricInfo>> metrics =
MetricParser.MetricAggregator.aggregateMetrics("default", emittedMetrics, targetScopeName);
Map<String, List<EmittedMetrics.Metric>> result =
MetricParser.MetricAggregator.buildFilteredMetrics(metrics);
assertThat(result.get("default")).hasSize(1);
assertThat(result.get("default").get(0).getName()).isEqualTo("my.metric1");
assertThat(metricNames).hasSize(2);
assertThat(metricNames).containsExactly("metric1", "metric2");
}
@Test
void testAggregatesAndDeduplicatesAttributes() {
String targetScopeName = "my-instrumentation-scope";
void parseMetricsHandlesEmptyInput() {
String input = "metrics:\n";
Map<String, StringBuilder> metricMap = new HashMap<>();
metricMap.put("default", new StringBuilder(input));
EmittedMetrics.Metric metric1 =
new EmittedMetrics.Metric(
"my.metric1",
"desc1",
"gauge",
"unit",
List.of(
new TelemetryAttribute("attr1", "STRING"),
new TelemetryAttribute("attr2", "STRING")));
EmittedMetrics.Metric metric1Dup =
new EmittedMetrics.Metric(
"my.metric1",
"desc1",
"gauge",
"unit",
List.of(
new TelemetryAttribute("attr1", "STRING"),
new TelemetryAttribute("attr3", "STRING")));
EmittedMetrics.MetricsByScope targetMetricsByScope =
new EmittedMetrics.MetricsByScope(targetScopeName, List.of(metric1, metric1Dup));
EmittedMetrics emittedMetrics = new EmittedMetrics("default", List.of(targetMetricsByScope));
Map<String, Map<String, MetricParser.MetricAggregator.AggregatedMetricInfo>> metrics =
MetricParser.MetricAggregator.aggregateMetrics("default", emittedMetrics, targetScopeName);
Map<String, List<EmittedMetrics.Metric>> result =
MetricParser.MetricAggregator.buildFilteredMetrics(metrics);
List<TelemetryAttribute> attrs = result.get("default").get(0).getAttributes();
assertThat(attrs).hasSize(3);
assertThat(attrs.stream().map(TelemetryAttribute::getName))
.containsExactlyInAnyOrder("attr1", "attr2", "attr3");
Map<String, EmittedMetrics> result = MetricParser.parseMetrics(metricMap);
assertThat(result).isEmpty();
}
@Test
void testPreservesMetricMetadata() {
String targetScopeName = "my-instrumentation-scope";
void getMetricsFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException {
Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry"));
EmittedMetrics.Metric metric1 =
createMetric("my.metric1", "description of my.metric1", "attr1");
String file1Content = "when: default\n metrics:\n - name: metric1\n type: counter\n";
String file2Content = "when: default\n metrics:\n - name: metric2\n type: gauge\n";
EmittedMetrics.MetricsByScope targetMetricsByScope =
new EmittedMetrics.MetricsByScope(targetScopeName, List.of(metric1));
Files.writeString(telemetryDir.resolve("metrics-1.yaml"), file1Content);
Files.writeString(telemetryDir.resolve("metrics-2.yaml"), file2Content);
EmittedMetrics emittedMetrics = new EmittedMetrics("default", List.of(targetMetricsByScope));
// Create a non-metrics file that should be ignored
Files.writeString(telemetryDir.resolve("other-file.yaml"), "some content");
Map<String, Map<String, MetricParser.MetricAggregator.AggregatedMetricInfo>> metrics =
MetricParser.MetricAggregator.aggregateMetrics("default", emittedMetrics, targetScopeName);
try (MockedStatic<FileManager> 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);
Map<String, List<EmittedMetrics.Metric>> result =
MetricParser.MetricAggregator.buildFilteredMetrics(metrics);
Map<String, EmittedMetrics> result = MetricParser.getMetricsFromFiles(tempDir.toString(), "");
EmittedMetrics.Metric foundMetric = result.get("default").get(0);
assertThat(foundMetric.getName()).isEqualTo("my.metric1");
assertThat(foundMetric.getDescription()).isEqualTo("description of my.metric1");
assertThat(foundMetric.getType()).isEqualTo("gauge");
assertThat(foundMetric.getUnit()).isEqualTo("unit");
EmittedMetrics metrics = result.get("default");
assertThat(metrics.getMetrics()).hasSize(2);
List<String> metricNames =
metrics.getMetrics().stream().map(EmittedMetrics.Metric::getName).sorted().toList();
assertThat(metricNames).containsExactly("metric1", "metric2");
}
}
private static EmittedMetrics.Metric createMetric(
String name, String description, String attrName) {
return new EmittedMetrics.Metric(
name, description, "gauge", "unit", List.of(new TelemetryAttribute(attrName, "STRING")));
@Test
void getMetricsFromFilesHandlesNonexistentDirectory() {
Map<String, EmittedMetrics> result = MetricParser.getMetricsFromFiles("/nonexistent", "path");
assertThat(result).isEmpty();
}
}

View File

@ -1,198 +0,0 @@
/*
* 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.EmittedSpans;
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
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 java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.MockedStatic;
@SuppressWarnings("NullAway")
class SpanParserTest {
@Test
void getSpansFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException {
Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry"));
String file1Content =
"""
when: default
spans_by_scope:
- scope: test
spans:
- span_kind: INTERNAL
attributes:
- scope: io.opentelemetry.clickhouse-client-0.5
spans:
- span_kind: SERVER
attributes:
- name: server.address
type: STRING
""";
String file2Content =
"""
when: default
spans_by_scope:
- scope: test
spans:
- span_kind: INTERNAL
attributes:
- scope: io.opentelemetry.clickhouse-client-0.5
spans:
- span_kind: CLIENT
attributes:
- name: db.statement
type: STRING
- name: server.port
type: LONG
- name: db.system
type: STRING
- name: server.address
type: STRING
- name: db.name
type: STRING
- name: db.operation
type: STRING
""";
Files.writeString(telemetryDir.resolve("spans-1.yaml"), file1Content);
Files.writeString(telemetryDir.resolve("spans-2.yaml"), file2Content);
// duplicate span contents to test deduplication
Files.writeString(telemetryDir.resolve("spans-3.yaml"), file2Content);
try (MockedStatic<FileManager> fileManagerMock = mockStatic(FileManager.class)) {
fileManagerMock
.when(() -> FileManager.readFileToString(telemetryDir.resolve("spans-1.yaml").toString()))
.thenReturn(file1Content);
fileManagerMock
.when(() -> FileManager.readFileToString(telemetryDir.resolve("spans-2.yaml").toString()))
.thenReturn(file2Content);
fileManagerMock
.when(() -> FileManager.readFileToString(telemetryDir.resolve("spans-3.yaml").toString()))
.thenReturn(file2Content);
Map<String, EmittedSpans> result =
EmittedSpanParser.getSpansByScopeFromFiles(tempDir.toString(), "");
EmittedSpans spans = result.get("default");
assertThat(spans.getSpansByScope()).hasSize(2);
List<EmittedSpans.Span> clickHouseSpans =
spans.getSpansByScope().stream()
.filter(item -> item.getScope().equals("io.opentelemetry.clickhouse-client-0.5"))
.map(EmittedSpans.SpansByScope::getSpans)
.findFirst()
.orElse(null);
assertThat(clickHouseSpans).hasSize(2);
EmittedSpans.Span serverSpan =
clickHouseSpans.stream()
.filter(item -> item.getSpanKind().equals("SERVER"))
.findFirst()
.orElse(null);
EmittedSpans.Span clientSpan =
clickHouseSpans.stream()
.filter(item -> item.getSpanKind().equals("CLIENT"))
.findFirst()
.orElse(null);
assertThat(serverSpan.getAttributes()).hasSize(1);
assertThat(clientSpan.getAttributes()).hasSize(6);
List<EmittedSpans.Span> testSpans =
spans.getSpansByScope().stream()
.filter(item -> item.getScope().equals("test"))
.map(EmittedSpans.SpansByScope::getSpans)
.findFirst()
.orElse(null);
// deduped should have only one span
assertThat(testSpans).hasSize(1);
}
}
@Test
void testSpanAggregatorFiltersAndAggregatesCorrectly() {
String targetScopeName = "my-instrumentation-scope";
EmittedSpans.Span span1 =
new EmittedSpans.Span(
"CLIENT",
List.of(
new TelemetryAttribute("my.operation", "STRING"),
new TelemetryAttribute("http.request.header.x-test-request", "STRING_ARRAY"),
new TelemetryAttribute("http.response.header.x-test-response", "STRING_ARRAY")));
EmittedSpans.Span span2 =
new EmittedSpans.Span(
"SERVER",
List.of(
new TelemetryAttribute("my.operation", "STRING"),
new TelemetryAttribute("test-baggage-key-1", "STRING"),
new TelemetryAttribute("test-baggage-key-2", "STRING")));
// Create test span for a different scope (should be filtered out)
EmittedSpans.Span testSpan =
new EmittedSpans.Span(
"INTERNAL", List.of(new TelemetryAttribute("my.operation", "STRING")));
EmittedSpans.SpansByScope targetSpansByScope =
new EmittedSpans.SpansByScope(targetScopeName, List.of(span1, span2));
EmittedSpans.SpansByScope otherSpansByScope =
new EmittedSpans.SpansByScope("other-scope", List.of(testSpan));
EmittedSpans emittedSpans =
new EmittedSpans("default", List.of(targetSpansByScope, otherSpansByScope));
// Aggregate spans - only target scope should be included
Map<String, Map<String, Set<TelemetryAttribute>>> spans =
SpanParser.SpanAggregator.aggregateSpans("default", emittedSpans, targetScopeName);
Map<String, List<EmittedSpans.Span>> result =
SpanParser.SpanAggregator.buildFilteredSpans(spans);
assertThat(result.size()).isEqualTo(1);
assertThat(result.get("default")).isNotNull();
assertThat(result.get("default").size()).isEqualTo(2); // CLIENT and SERVER spans
// Verify span kinds are preserved
List<String> spanKinds =
result.get("default").stream().map(EmittedSpans.Span::getSpanKind).toList();
assertThat(spanKinds).containsExactlyInAnyOrder("CLIENT", "SERVER");
// Verify test attributes are filtered out
List<TelemetryAttribute> clientAttributes =
result.get("default").stream()
.filter(span -> span.getSpanKind().equals("CLIENT"))
.flatMap(span -> span.getAttributes().stream())
.toList();
assertThat(clientAttributes)
.hasSize(1)
.extracting(TelemetryAttribute::getName)
.containsExactly("my.operation");
List<TelemetryAttribute> serverAttributes =
result.get("default").stream()
.filter(span -> span.getSpanKind().equals("SERVER"))
.flatMap(span -> span.getAttributes().stream())
.toList();
assertThat(serverAttributes)
.hasSize(1)
.extracting(TelemetryAttribute::getName)
.containsExactly("my.operation");
}
}

View File

@ -11,12 +11,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import io.opentelemetry.instrumentation.docs.internal.ConfigurationOption;
import io.opentelemetry.instrumentation.docs.internal.ConfigurationType;
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
import java.io.BufferedWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@ -299,11 +297,11 @@ class YamlHelperTest {
"HISTOGRAM",
"s",
List.of(
new TelemetryAttribute("db.namespace", "STRING"),
new TelemetryAttribute("db.operation.name", "STRING"),
new TelemetryAttribute("db.system.name", "STRING"),
new TelemetryAttribute("server.address", "STRING"),
new TelemetryAttribute("server.port", "LONG")));
new EmittedMetrics.Attribute("db.namespace", "STRING"),
new EmittedMetrics.Attribute("db.operation.name", "STRING"),
new EmittedMetrics.Attribute("db.system.name", "STRING"),
new EmittedMetrics.Attribute("server.address", "STRING"),
new EmittedMetrics.Attribute("server.port", "LONG")));
targetVersions.put(
InstrumentationType.LIBRARY, new HashSet<>(List.of("org.apache.mylib:mylib-core:2.3.0")));
@ -356,68 +354,4 @@ class YamlHelperTest {
assertThat(expectedYaml).isEqualTo(stringWriter.toString());
}
@Test
void testSpanParsing() throws Exception {
List<InstrumentationModule> modules = new ArrayList<>();
Map<InstrumentationType, Set<String>> targetVersions = new HashMap<>();
EmittedSpans.Span span =
new EmittedSpans.Span(
"CLIENT",
List.of(
new TelemetryAttribute("db.namespace", "STRING"),
new TelemetryAttribute("db.operation.name", "STRING"),
new TelemetryAttribute("db.system.name", "STRING"),
new TelemetryAttribute("server.address", "STRING"),
new TelemetryAttribute("server.port", "LONG")));
targetVersions.put(
InstrumentationType.LIBRARY, new HashSet<>(List.of("org.apache.mylib:mylib-core:2.3.0")));
modules.add(
new InstrumentationModule.Builder()
.srcPath("instrumentation/mylib/mylib-core-2.3")
.instrumentationName("mylib-2.3")
.namespace("mylib")
.group("mylib")
.targetVersions(targetVersions)
.spans(Map.of("default", List.of(span)))
.build());
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
YamlHelper.generateInstrumentationYaml(modules, writer);
writer.flush();
String expectedYaml =
"""
libraries:
mylib:
- name: mylib-2.3
source_path: instrumentation/mylib/mylib-core-2.3
scope:
name: io.opentelemetry.mylib-2.3
target_versions:
library:
- org.apache.mylib:mylib-core:2.3.0
telemetry:
- when: default
spans:
- span_kind: CLIENT
attributes:
- name: db.namespace
type: STRING
- name: db.operation.name
type: STRING
- name: db.system.name
type: STRING
- name: server.address
type: STRING
- name: server.port
type: LONG
""";
assertThat(expectedYaml).isEqualTo(stringWriter.toString());
}
}

View File

@ -19,10 +19,3 @@ dependencies {
otelJava {
minJavaVersionSupported.set(JavaVersion.VERSION_17)
}
tasks {
test {
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -5,6 +5,11 @@
package io.opentelemetry.instrumentation.akkaactor
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import akka.actor.{Actor, ActorLogging, ActorRef, ActorSystem, Props}
import akka.pattern.ask
import akka.util.Timeout

View File

@ -66,7 +66,6 @@ tasks {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
check {

View File

@ -27,12 +27,10 @@ tasks {
systemProperty("collectMetadata", collectMetadata)
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
systemProperty("collectSpans", true)
}
test {
systemProperty("collectMetadata", collectMetadata)
systemProperty("collectSpans", true)
}
check {

View File

@ -1,7 +0,0 @@
description: >
This instrumentation provides database connection pools metrics for Apache DBCP.
The instrumentation uses `MBeanRegistration` methods for lifecycle detection, therefore it
only activates if the `BasicDataSource` is registered to an `MBeanServer`. If using Spring Boot,
this happens automatically as all Spring beans that support JMX registration are automatically
registered by default.

View File

@ -45,9 +45,6 @@ tasks.withType<Test>().configureEach {
jvmArgs("--add-opens=java.base/java.math=ALL-UNNAMED")
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
tasks {

View File

@ -20,6 +20,5 @@ dependencies {
tasks {
test {
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1 +0,0 @@
description: This instrumentation provides CLIENT spans and metrics for the Apache HttpAsyncClient.

View File

@ -19,7 +19,5 @@ dependencies {
tasks {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1 +0,0 @@
description: This instrumentation provides CLIENT spans and metrics for versions 2 and 3 of the Apache HttpClient.

View File

@ -29,10 +29,3 @@ dependencies {
library("org.apache.httpcomponents:httpclient:4.0")
testCompileOnly("net.jcip:jcip-annotations:1.0")
}
tasks {
test {
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1 +0,0 @@
description: This instrumentation provides CLIENT spans and metrics for version 4 of the Apache HttpClient.

View File

@ -11,10 +11,3 @@ dependencies {
latestDepTestLibrary("org.apache.httpcomponents:httpclient:4.+") // see apache-httpclient-5.0 module
}
tasks {
test {
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
systemProperty("collectSpans", true)
}
}

View File

@ -1 +0,0 @@
description: This instrumentation provides a library integration that enables CLIENT spans and metrics for the Apache HttpClient.

View File

@ -99,39 +99,33 @@ class AwsConnector {
}
void setTopicPublishingPolicy(String topicArn) {
String snsPolicy =
"{"
+ " \"Statement\": ["
+ " {"
+ " \"Effect\": \"Allow\","
+ " \"Principal\": \"*\","
+ " \"Action\": \"sns:Publish\","
+ " \"Resource\": \"%s\""
+ " }]"
+ "}";
snsClient.setTopicAttributes(
new SetTopicAttributesRequest(
topicArn,
"Policy",
String.format(
"{"
+ " \"Statement\": ["
+ " {"
+ " \"Effect\": \"Allow\","
+ " \"Principal\": \"*\","
+ " \"Action\": \"sns:Publish\","
+ " \"Resource\": \"%s\""
+ " }]"
+ "}",
topicArn)));
new SetTopicAttributesRequest(topicArn, "Policy", String.format(snsPolicy, topicArn)));
}
void setQueuePublishingPolicy(String queueUrl, String queueArn) {
String sqsPolicy =
"{"
+ " \"Statement\": ["
+ " {"
+ " \"Effect\": \"Allow\","
+ " \"Principal\": \"*\","
+ " \"Action\": \"sqs:SendMessage\","
+ " \"Resource\": \"%s\""
+ " }]"
+ "}";
sqsClient.setQueueAttributes(
queueUrl,
Collections.singletonMap(
"Policy",
String.format(
"{"
+ " \"Statement\": ["
+ " {"
+ " \"Effect\": \"Allow\","
+ " \"Principal\": \"*\","
+ " \"Action\": \"sqs:SendMessage\","
+ " \"Resource\": \"%s\""
+ " }]"
+ "}",
queueArn)));
queueUrl, Collections.singletonMap("Policy", String.format(sqsPolicy, queueArn)));
}
void createBucket(String bucketName) {

View File

@ -23,8 +23,7 @@ dependencies {
latestDepTestLibrary("com.amazonaws:aws-java-sdk-sqs:1.12.583") // documented limitation
}
val testLatestDeps = findProperty("testLatestDeps") as Boolean
if (!testLatestDeps) {
if (!(findProperty("testLatestDeps") as Boolean)) {
configurations.testRuntimeClasspath {
resolutionStrategy {
eachDependency {
@ -37,26 +36,12 @@ if (!testLatestDeps) {
}
}
testing {
suites {
val testSecretsManager by registering(JvmTestSuite::class) {
dependencies {
implementation(project())
implementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing"))
val version = if (testLatestDeps) "latest.release" else "1.12.80"
implementation("com.amazonaws:aws-java-sdk-secretsmanager:$version")
}
}
}
}
tasks {
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
}
check {
dependsOn(testing.suites)
dependsOn(testStableSemconv)
}
}

View File

@ -24,8 +24,6 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
private static final AttributeKey<String> AWS_REQUEST_ID = stringKey("aws.request_id");
// Copied from AwsIncubatingAttributes
private static final AttributeKey<String> AWS_SECRETSMANAGER_SECRET_ARN =
stringKey("aws.secretsmanager.secret.arn");
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_ACTIVITY_ARN =
stringKey("aws.step_functions.activity.arn");
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN =
@ -64,9 +62,8 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
Request<?> request,
@Nullable Response<?> response,
@Nullable Throwable error) {
Object awsResp = getAwsResponse(response);
if (awsResp != null) {
setAttribute(attributes, AWS_SECRETSMANAGER_SECRET_ARN, awsResp, RequestAccess::getSecretArn);
if (response != null) {
Object awsResp = response.getAwsResponse();
setAttribute(
attributes,
AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN,
@ -109,11 +106,4 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
attributes.put(key, value);
}
}
private static Object getAwsResponse(Response<?> response) {
if (response == null) {
return null;
}
return response.getAwsResponse();
}
}

View File

@ -11,8 +11,6 @@ import java.lang.invoke.MethodType;
import javax.annotation.Nullable;
final class RequestAccess {
private static final String SECRETS_MANAGER_REQUEST_CLASS_PREFIX =
"com.amazonaws.services.secretsmanager.model.";
private static final String STEP_FUNCTIONS_REQUEST_CLASS_PREFIX =
"com.amazonaws.services.stepfunctions.model.";
@ -24,20 +22,20 @@ final class RequestAccess {
}
};
@Nullable
static String getSecretArn(Object request) {
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
return invokeOrNull(access.getSecretArn, request);
}
@Nullable
static String getStepFunctionsActivityArn(Object request) {
if (request == null) {
return null;
}
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
return invokeOrNull(access.getStepFunctionsActivityArn, request);
}
@Nullable
static String getStateMachineArn(Object request) {
if (request == null) {
return null;
}
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
return invokeOrNull(access.getStateMachineArn, request);
}
@ -99,7 +97,6 @@ final class RequestAccess {
@Nullable private final MethodHandle getBucketName;
@Nullable private final MethodHandle getQueueUrl;
@Nullable private final MethodHandle getQueueName;
@Nullable private final MethodHandle getSecretArn;
@Nullable private final MethodHandle getStreamName;
@Nullable private final MethodHandle getTableName;
@Nullable private final MethodHandle getTopicArn;
@ -115,9 +112,6 @@ final class RequestAccess {
getTableName = findAccessorOrNull(clz, "getTableName");
getTopicArn = findAccessorOrNull(clz, "getTopicArn");
getTargetArn = findAccessorOrNull(clz, "getTargetArn");
boolean isSecretsManager = clz.getName().startsWith(SECRETS_MANAGER_REQUEST_CLASS_PREFIX);
getSecretArn = isSecretsManager ? findAccessorOrNull(clz, "getARN") : null;
boolean isStepFunction = clz.getName().startsWith(STEP_FUNCTIONS_REQUEST_CLASS_PREFIX);
getStateMachineArn = isStepFunction ? findAccessorOrNull(clz, "getStateMachineArn") : null;
getStepFunctionsActivityArn = isStepFunction ? findAccessorOrNull(clz, "getActivityArn") : null;

View File

@ -81,7 +81,7 @@ final class TracingRequestHandler extends RequestHandler2 {
Context context = InstrumenterUtil.suppressSpan(instrumenter, parentContext, request);
context = context.with(REQUEST_TIMER_KEY, Timer.start());
context = context.with(PARENT_CONTEXT_KEY, parentContext);
context = context.with(REQUEST_SPAN_SUPPRESSED_KEY, true);
context = context.with(REQUEST_SPAN_SUPPRESSED_KEY, Boolean.TRUE);
request.addHandlerContext(CONTEXT, context);
return;
}

View File

@ -1,32 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.awssdk.v1_11;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
class SecretsManagerClientTest extends AbstractSecretsManagerClientTest {
@RegisterExtension
private static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Override
protected InstrumentationExtension testing() {
return testing;
}
@Override
public AWSSecretsManagerClientBuilder configureClient(
AWSSecretsManagerClientBuilder clientBuilder) {
return clientBuilder.withRequestHandlers(
AwsSdkTelemetry.builder(testing().getOpenTelemetry())
.setCaptureExperimentalSpanAttributes(true)
.build()
.newRequestHandler());
}
}

View File

@ -12,7 +12,6 @@ dependencies {
compileOnly("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
compileOnly("com.amazonaws:aws-java-sdk-rds:1.11.106")
compileOnly("com.amazonaws:aws-java-sdk-s3:1.11.106")
compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.12.80")
compileOnly("com.amazonaws:aws-java-sdk-sns:1.11.106")
compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106")
compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.106")

View File

@ -1,93 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.awssdk.v1_11;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_SECRETSMANAGER_SECRET_ARN;
import static java.util.Collections.singletonList;
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.CreateSecretRequest;
import com.amazonaws.services.secretsmanager.model.DescribeSecretRequest;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
import io.opentelemetry.testing.internal.armeria.common.MediaType;
import java.util.List;
import org.junit.jupiter.api.Test;
public abstract class AbstractSecretsManagerClientTest extends AbstractBaseAwsClientTest {
public abstract AWSSecretsManagerClientBuilder configureClient(
AWSSecretsManagerClientBuilder client);
@Override
protected boolean hasRequestId() {
return false;
}
@Test
public void sendCreateSecretRequestWithMockedResponse() throws Exception {
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
AWSSecretsManager client =
configureClient(clientBuilder)
.withEndpointConfiguration(endpoint)
.withCredentials(credentialsProvider)
.build();
String body =
"{"
+ "\"ARN\": \"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3\","
+ "\"Name\": \"MyTestDatabaseSecret\","
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
+ "}";
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
Object response =
client.createSecret(
new CreateSecretRequest().withName("secretName").withSecretString("secretValue"));
List<AttributeAssertion> additionalAttributes =
singletonList(
equalTo(
AWS_SECRETSMANAGER_SECRET_ARN,
"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3"));
assertRequestWithMockedResponse(
response, client, "AWSSecretsManager", "CreateSecret", "POST", additionalAttributes);
}
@Test
public void sendDescribeSecretRequestWithMockedResponse() throws Exception {
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
AWSSecretsManager client =
configureClient(clientBuilder)
.withEndpointConfiguration(endpoint)
.withCredentials(credentialsProvider)
.build();
String body =
"{"
+ "\"ARN\": \"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar\","
+ "\"Name\": \"My-Secret-Id\","
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
+ "}";
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
Object response =
client.describeSecret(new DescribeSecretRequest().withSecretId("My-Secret-Id"));
List<AttributeAssertion> additionalAttributes =
singletonList(
equalTo(
AWS_SECRETSMANAGER_SECRET_ARN,
"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar"));
assertRequestWithMockedResponse(
response, client, "AWSSecretsManager", "DescribeSecret", "POST", additionalAttributes);
}
}

View File

@ -28,15 +28,12 @@ tasks {
test {
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
systemProperty("collectMetadata", collectMetadata)
systemProperty("collectSpans", true)
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
systemProperty("collectMetadata", collectMetadata)
systemProperty("collectSpans", true)
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
}
check {

View File

@ -78,7 +78,7 @@ tasks {
}
val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database,code")
jvmArgs("-Dotel.semconv-stability.opt-in=database")
}
check {

View File

@ -74,7 +74,7 @@ class Elasticsearch53NodeClientTest extends AbstractElasticsearchNodeClientTest
new ClusterUpdateSettingsRequest()
.transientSettings(
Collections.singletonMap(
"cluster.routing.allocation.disk.threshold_enabled", false)));
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
});
testing.waitForTraces(1);
testing.clearData();

View File

@ -87,7 +87,7 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC
new ClusterUpdateSettingsRequest()
.transientSettings(
Collections.singletonMap(
"cluster.routing.allocation.disk.threshold_enabled", false)));
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
});
testing.waitForTraces(1);
testing.clearData();

View File

@ -71,7 +71,7 @@ class Config {
new ClusterUpdateSettingsRequest()
.transientSettings(
Collections.singletonMap(
"cluster.routing.allocation.disk.threshold_enabled", false)));
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
return testNode;
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3.
import static io.opentelemetry.api.common.AttributeKey.longKey;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFunctionAssertions;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
@ -18,10 +17,9 @@ import io.opentelemetry.api.trace.SpanKind;
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.AttributeAssertion;
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.io.File;
import java.util.List;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -81,7 +79,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.findAll")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("findAll")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findAll")),
span ->
span.hasName("SearchAction")
.hasKind(SpanKind.CLIENT)
@ -111,7 +113,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.index")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("index")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "index")),
span ->
span.hasName("IndexAction")
.hasKind(SpanKind.CLIENT)
@ -156,7 +162,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.findById")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("findById")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findById")),
span ->
span.hasName("GetAction")
.hasKind(SpanKind.CLIENT)
@ -186,7 +196,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.index")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("index")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "index")),
span ->
span.hasName("IndexAction")
.hasKind(SpanKind.CLIENT)
@ -226,7 +240,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.findById")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("findById")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findById")),
span ->
span.hasName("GetAction")
.hasKind(SpanKind.CLIENT)
@ -254,7 +272,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.deleteById")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("deleteById")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "deleteById")),
span ->
span.hasName("DeleteAction")
.hasKind(SpanKind.CLIENT)
@ -293,7 +315,11 @@ class Elasticsearch53SpringRepositoryTest {
span.hasName("DocRepository.findAll")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(assertFunctionName("findAll")),
.hasAttributesSatisfyingExactly(
equalTo(
CodeIncubatingAttributes.CODE_NAMESPACE,
DocRepository.class.getName()),
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findAll")),
span ->
span.hasName("SearchAction")
.hasKind(SpanKind.CLIENT)
@ -308,8 +334,4 @@ class Elasticsearch53SpringRepositoryTest {
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
equalTo(stringKey("elasticsearch.request.search.types"), "doc"))));
}
private static List<AttributeAssertion> assertFunctionName(String methodName) {
return codeFunctionAssertions(DocRepository.class, methodName);
}
}

View File

@ -140,7 +140,7 @@ class Elasticsearch53SpringTemplateTest {
new ClusterUpdateSettingsRequest()
.transientSettings(
Collections.singletonMap(
"cluster.routing.allocation.disk.threshold_enabled", false)));
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
});
testing.waitForTraces(1);
testing.clearData();

View File

@ -68,7 +68,7 @@ public abstract class AbstractElasticsearch6NodeClientTest
new ClusterUpdateSettingsRequest()
.transientSettings(
Collections.singletonMap(
"cluster.routing.allocation.disk.threshold_enabled", false)));
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
});
testing.waitForTraces(1);
testing.clearData();

Some files were not shown because too many files have changed in this diff Show More