diff --git a/.github/renovate.json5 b/.github/renovate.json5 index c93741ed8db4..db5d36725457 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -124,7 +124,7 @@ ], "matchPackageNames": [ "org.slf4j:slf4j-api", - "org.springframework.boot", + "org.springframework.boot:org.springframework.boot.gradle.plugin", // this is for plugin id "org.springframework.boot" "org.springframework.boot:spring-boot-dependencies"], "matchUpdateTypes": ["major", "minor"], "enabled": false, @@ -163,7 +163,7 @@ { // intentionally using scala 2.11 in otel.scala-conventions.gradle.kts "matchFileNames": ["conventions/src/main/kotlin/otel.scala-conventions.gradle.kts"], - "matchPackagePrefixes": ["org.scala-lang:scala-library"], + "matchPackageNames": ["org.scala-lang:scala-library"], "matchUpdateTypes": ["major", "minor"], "enabled": false }, diff --git a/.github/workflows/auto-update-otel-sdk.yml b/.github/workflows/auto-update-otel-sdk.yml index 7fdd8c5994af..fffa5d618031 100644 --- a/.github/workflows/auto-update-otel-sdk.yml +++ b/.github/workflows/auto-update-otel-sdk.yml @@ -72,7 +72,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Update license report run: ./gradlew generateLicenseReport diff --git a/.github/workflows/build-common.yml b/.github/workflows/build-common.yml index 79efebc87490..437f71737cb3 100644 --- a/.github/workflows/build-common.yml +++ b/.github/workflows/build-common.yml @@ -38,7 +38,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} # gradle enterprise is used for the build cache @@ -54,7 +54,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: gradle/actions/wrapper-validation@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + - uses: gradle/actions/wrapper-validation@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 license-check: runs-on: ubuntu-latest @@ -71,7 +71,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} # gradle enterprise is used for the build cache @@ -144,7 +144,7 @@ jobs: sed -i "s/org.gradle.jvmargs=/org.gradle.jvmargs=-Xmx3g /" gradle.properties - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} # gradle enterprise is used for the build cache @@ -171,7 +171,7 @@ jobs: fi - name: Upload agent jar - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: opentelemetry-javaagent.jar path: javaagent/build/libs/opentelemetry-javaagent-*-SNAPSHOT.jar @@ -182,7 +182,7 @@ jobs: mkdir sboms cp javaagent/build/spdx/*.spdx.json sboms - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 name: Upload SBOMs with: name: opentelemetry-java-instrumentation-SBOM.zip @@ -249,7 +249,7 @@ jobs: run: .github/scripts/deadlock-detector.sh - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: # only push cache for one matrix option since github action cache space is limited cache-read-only: ${{ inputs.cache-read-only || matrix.test-java-version != 11 || matrix.vm != 'hotspot' }} @@ -291,7 +291,7 @@ jobs: - name: Upload deadlock detector artifacts if any if: failure() - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: deadlock-detector-test-${{ matrix.test-java-version }}-${{ matrix.vm }}-${{ matrix.test-partition }} path: /tmp/deadlock-detector-* @@ -299,7 +299,7 @@ jobs: - name: Upload jvm crash dump files if any if: failure() - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: javacore-test-${{ matrix.test-java-version }}-${{ matrix.test-partition }} path: | @@ -348,7 +348,7 @@ jobs: java-version-file: .java-version - name: Set up Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: # only push cache for one matrix option per OS since github action cache space is limited cache-read-only: ${{ inputs.cache-read-only || matrix.smoke-test-suite != 'tomcat' }} @@ -368,7 +368,7 @@ jobs: - name: Upload jvm crash dump files if any if: failure() - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: javacore-smoke-test-${{ matrix.smoke-test-suite }}-${{ matrix.os }} # we expect crash dumps either in root director or in smoke-tests @@ -401,7 +401,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} @@ -424,7 +424,7 @@ jobs: java-version-file: .java-version - name: Set up Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 214f30cb4ef6..3e836e28a64d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,7 +73,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: # gradle enterprise is used for the build cache gradle-home-cache-excludes: caches/build-cache-1 diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 658038dc362d..597f665bf3eb 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -30,7 +30,7 @@ jobs: java-version-file: .java-version - name: Initialize CodeQL - uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: languages: java # using "latest" helps to keep up with the latest Kotlin support @@ -38,14 +38,14 @@ jobs: tools: latest - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Build # skipping build cache is needed so that all modules will be analyzed run: ./gradlew assemble -x javadoc --no-build-cache --no-daemon - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 workflow-notification: needs: diff --git a/.github/workflows/overhead-benchmark-daily.yml b/.github/workflows/overhead-benchmark-daily.yml index cc62b80ed000..7533ecade040 100644 --- a/.github/workflows/overhead-benchmark-daily.yml +++ b/.github/workflows/overhead-benchmark-daily.yml @@ -24,7 +24,7 @@ jobs: rsync -avv gh-pages/benchmark-overhead/results/ benchmark-overhead/results/ - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Run tests working-directory: benchmark-overhead diff --git a/.github/workflows/owasp-dependency-check-daily.yml b/.github/workflows/owasp-dependency-check-daily.yml index 2d44702e2024..4201121665da 100644 --- a/.github/workflows/owasp-dependency-check-daily.yml +++ b/.github/workflows/owasp-dependency-check-daily.yml @@ -28,7 +28,7 @@ jobs: run: | sed -i "s/org.gradle.jvmargs=/org.gradle.jvmargs=-Xmx3g /" gradle.properties - - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - run: ./gradlew :javaagent:dependencyCheckAnalyze env: @@ -36,7 +36,7 @@ jobs: - name: Upload report if: always() - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: path: javaagent/build/reports diff --git a/.github/workflows/pr-smoke-test-early-jdk8-images.yml b/.github/workflows/pr-smoke-test-early-jdk8-images.yml index e29c168590f2..53d884c992cc 100644 --- a/.github/workflows/pr-smoke-test-early-jdk8-images.yml +++ b/.github/workflows/pr-smoke-test-early-jdk8-images.yml @@ -25,7 +25,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: true # gradle enterprise is used for the build cache diff --git a/.github/workflows/pr-smoke-test-fake-backend-images.yml b/.github/workflows/pr-smoke-test-fake-backend-images.yml index 75bb09623157..b30cc661794f 100644 --- a/.github/workflows/pr-smoke-test-fake-backend-images.yml +++ b/.github/workflows/pr-smoke-test-fake-backend-images.yml @@ -25,7 +25,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: true # gradle enterprise is used for the build cache @@ -52,7 +52,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: true diff --git a/.github/workflows/pr-smoke-test-servlet-images.yml b/.github/workflows/pr-smoke-test-servlet-images.yml index 52662d3943b4..bbaf5e726883 100644 --- a/.github/workflows/pr-smoke-test-servlet-images.yml +++ b/.github/workflows/pr-smoke-test-servlet-images.yml @@ -31,19 +31,19 @@ jobs: run: git config --system core.longpaths true if: matrix.os == 'windows-latest' - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@v4 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: temurin java-version-file: .java-version - name: Set up Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: true diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 41e0ed30a37e..9e68c238ec9b 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -6,7 +6,7 @@ jobs: prepare-patch-release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: | if [[ ! $GITHUB_REF_NAME =~ ^release/v[0-9]+\.[0-9]+\.x$ ]]; then diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml index ebf158f66dfc..27de6a58c3e1 100644 --- a/.github/workflows/prepare-release-branch.yml +++ b/.github/workflows/prepare-release-branch.yml @@ -6,7 +6,7 @@ jobs: prereqs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Verify prerequisites run: | @@ -25,7 +25,7 @@ jobs: needs: - prereqs steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Create release branch run: | @@ -78,7 +78,7 @@ jobs: needs: - prereqs steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set environment variables run: | diff --git a/.github/workflows/publish-petclinic-benchmark-image.yml b/.github/workflows/publish-petclinic-benchmark-image.yml index 9646756ff7db..0b3cf639c01a 100644 --- a/.github/workflows/publish-petclinic-benchmark-image.yml +++ b/.github/workflows/publish-petclinic-benchmark-image.yml @@ -14,12 +14,12 @@ jobs: packages: write contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: docker/setup-buildx-action@v3 + - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Login to GitHub container registry - uses: docker/login-action@v3 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -29,7 +29,7 @@ jobs: run: echo "TS=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - name: Push to GitHub packages - uses: docker/build-push-action@v6 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: push: true file: benchmark-overhead/Dockerfile-petclinic-base diff --git a/.github/workflows/publish-smoke-test-early-jdk8-images.yml b/.github/workflows/publish-smoke-test-early-jdk8-images.yml index 8ba8425e28ec..b8e7d7ca3779 100644 --- a/.github/workflows/publish-smoke-test-early-jdk8-images.yml +++ b/.github/workflows/publish-smoke-test-early-jdk8-images.yml @@ -13,19 +13,19 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@v4 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: temurin java-version-file: .java-version - name: Login to GitHub package registry - uses: docker/login-action@v3 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -35,7 +35,7 @@ jobs: run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Build Docker image run: ./gradlew :smoke-tests:images:early-jdk8:dockerPush -PextraTag=${{ env.TAG }} diff --git a/.github/workflows/publish-smoke-test-fake-backend-images.yml b/.github/workflows/publish-smoke-test-fake-backend-images.yml index ffd8a242ad8a..20722beee7b3 100644 --- a/.github/workflows/publish-smoke-test-fake-backend-images.yml +++ b/.github/workflows/publish-smoke-test-fake-backend-images.yml @@ -13,19 +13,19 @@ jobs: publishLinux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@v4 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: temurin java-version-file: .java-version - name: Login to GitHub package registry - uses: docker/login-action@v3 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -35,7 +35,7 @@ jobs: run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Build Docker image run: ./gradlew :smoke-tests:images:fake-backend:jib -Djib.httpTimeout=120000 -Djib.console=plain -PextraTag=${{ env.TAG }} @@ -49,16 +49,16 @@ jobs: - name: Support long paths run: git config --system core.longpaths true - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up JDK for running Gradle - uses: actions/setup-java@v4 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: temurin java-version-file: .java-version - name: Login to GitHub package registry - uses: azure/docker-login@v2 + uses: azure/docker-login@15c4aadf093404726ab2ff205b2cdd33fa6d054c # v2 with: login-server: ghcr.io username: ${{ github.repository_owner }} @@ -68,7 +68,7 @@ jobs: run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Build Docker image run: ./gradlew :smoke-tests:images:fake-backend:dockerPush -PextraTag=${{ env.TAG }} diff --git a/.github/workflows/publish-smoke-test-servlet-images.yml b/.github/workflows/publish-smoke-test-servlet-images.yml index 98b12af63d50..ec096eb74d21 100644 --- a/.github/workflows/publish-smoke-test-servlet-images.yml +++ b/.github/workflows/publish-smoke-test-servlet-images.yml @@ -67,7 +67,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: # only push cache for one matrix option per OS since github action cache space is limited cache-read-only: ${{ matrix.smoke-test-suite != 'tomcat' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e86cd2ebe4f0..b3203a0e2dfe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Build and publish artifacts env: @@ -114,7 +114,7 @@ jobs: cp javaagent/build/spdx/*.spdx.json sboms zip opentelemetry-java-instrumentation-SBOM.zip sboms/* - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 name: Upload SBOMs with: name: opentelemetry-java-instrumentation-SBOM diff --git a/.github/workflows/reusable-muzzle.yml b/.github/workflows/reusable-muzzle.yml index 7ea42f3deaf2..7921906365e0 100644 --- a/.github/workflows/reusable-muzzle.yml +++ b/.github/workflows/reusable-muzzle.yml @@ -34,7 +34,7 @@ jobs: java-version-file: .java-version - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} diff --git a/.github/workflows/reusable-smoke-test-images.yml b/.github/workflows/reusable-smoke-test-images.yml index 73eeb4aa53a1..deba13c46d03 100644 --- a/.github/workflows/reusable-smoke-test-images.yml +++ b/.github/workflows/reusable-smoke-test-images.yml @@ -61,7 +61,7 @@ jobs: run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - name: Set up Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} diff --git a/.github/workflows/reusable-test-indy.yml b/.github/workflows/reusable-test-indy.yml index 2b2727ba8ea1..c73ce3644a2e 100644 --- a/.github/workflows/reusable-test-indy.yml +++ b/.github/workflows/reusable-test-indy.yml @@ -58,7 +58,7 @@ jobs: key: ${{ runner.os }}-test-latest-cache-pnpm-modules - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} # gradle enterprise is used for the build cache diff --git a/.github/workflows/reusable-test-latest-deps.yml b/.github/workflows/reusable-test-latest-deps.yml index 15f59f58c94c..03130333c9f8 100644 --- a/.github/workflows/reusable-test-latest-deps.yml +++ b/.github/workflows/reusable-test-latest-deps.yml @@ -55,7 +55,7 @@ jobs: run: .github/scripts/deadlock-detector.sh - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: ${{ inputs.cache-read-only }} # gradle enterprise is used for the build cache @@ -90,7 +90,7 @@ jobs: - name: Upload deadlock detector artifacts if any if: failure() - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: deadlock-detector-test-latest-${{ matrix.test-java-version }}-${{ matrix.vm }}-${{ matrix.test-partition }} path: /tmp/deadlock-detector-* @@ -98,7 +98,7 @@ jobs: - name: Upload jvm crash dump files if any if: failure() - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: javacore-test-latest-${{ matrix.test-java-version }}-${{ matrix.test-partition }} path: | diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 9644ee8d786d..568bbddc68e2 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -56,7 +56,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: SARIF file path: results.sarif @@ -64,6 +64,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index 442a8540ecde..c83a89d393da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,51 @@ ## Unreleased +## Version 1.33.6 (2024-08-26) + +### 📈 Enhancements + +- Backport: Update the OpenTelemetry SDK version to 1.41.0 + ([#12071](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/12071)) + +## Version 2.7.0 (2024-08-16) + +### 📈 Enhancements + +- Add span baggage processor + ([#11697](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11697)) +- Improve tomcat version detection + ([#11936](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11936)) +- Improve akka route handling with java dsl + ([#11926](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11926)) +- Ignore Alibaba fastjson ASMClassLoader + ([#11954](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11954)) +- Use `aws-lambda-java-serialization` library, which is available by default, while deserializing input and serializing output + ([#11868](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11868)) +- Logback appender: map timestamp in nanoseconds if possible + ([#11974](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11974)) +- Save ILoggingEvent.getArgumentArray() arguments from Logback + ([#11865](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11865)) +- Update Java 17-based metrics to stable semconv + ([#11914](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11914)) +- Add Pulsar Consumer metrics + ([#11891](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11891)) + +### 🛠️ Bug fixes + +- Fix missing throw statement in RestClientWrapper + ([#11893](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11893)) +- Fix ClickHouse tracing when database name not included in connection string + ([#11852](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11852)) +- Fix class cast exception, noop meter does not implement incubating API + ([#11934](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11934)) +- Closing a kafka producer/consumer should not disable metrics from other consumers/producers + ([#11975](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11975)) +- Fix ending span in Ktor plugin + ([#11726](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/11726)) +- Fix netty memory leak + ([#12003](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/12003)) + ## Version 1.33.5 (2024-07-25) ### 📈 Enhancements diff --git a/benchmark-overhead-jmh/build.gradle.kts b/benchmark-overhead-jmh/build.gradle.kts index 6e713e3db381..99a723959eec 100644 --- a/benchmark-overhead-jmh/build.gradle.kts +++ b/benchmark-overhead-jmh/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } dependencies { - jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.3.2") + jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.3.3") } tasks { diff --git a/benchmark-overhead/build.gradle.kts b/benchmark-overhead/build.gradle.kts index 9793e9c5cbf8..e43bab0693eb 100644 --- a/benchmark-overhead/build.gradle.kts +++ b/benchmark-overhead/build.gradle.kts @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation(enforcedPlatform("org.junit:junit-bom:5.10.3")) + implementation(enforcedPlatform("org.junit:junit-bom:5.11.0")) testImplementation("org.testcontainers:testcontainers:1.20.1") testImplementation("org.testcontainers:postgresql:1.20.1") diff --git a/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar b/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c..a4b76b9530d6 100644 Binary files a/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar and b/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar differ diff --git a/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties b/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties index 68e8816d71c9..2b189974c29a 100644 --- a/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties +++ b/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/buildscripts/checkstyle.xml b/buildscripts/checkstyle.xml index daa859a76e87..a3a4ff7d3512 100644 --- a/buildscripts/checkstyle.xml +++ b/buildscripts/checkstyle.xml @@ -50,6 +50,18 @@ --> + + + + + + + + diff --git a/conventions/build.gradle.kts b/conventions/build.gradle.kts index 1454ffa411b8..f7b3b498aba2 100644 --- a/conventions/build.gradle.kts +++ b/conventions/build.gradle.kts @@ -55,23 +55,23 @@ dependencies { // When updating, update above in plugins too implementation("com.diffplug.spotless:spotless-plugin-gradle:6.25.0") - implementation("com.google.guava:guava:33.2.1-jre") + implementation("com.google.guava:guava:33.3.0-jre") implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18") implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.0") implementation("org.apache.httpcomponents:httpclient:4.5.14") - implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:3.17.6") - implementation("org.owasp:dependency-check-gradle:10.0.3") + implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:3.18") + implementation("org.owasp:dependency-check-gradle:10.0.4") implementation("ru.vyarus:gradle-animalsniffer-plugin:1.7.1") implementation("org.spdx:spdx-gradle-plugin:0.8.0") // When updating, also update dependencyManagement/build.gradle.kts - implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.14.18") + implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.15.1") implementation("gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.6") implementation("me.champeau.jmh:jmh-gradle-plugin:0.7.2") implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.0.1") implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.0.0") implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.3") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.10.3")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.11.0")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testImplementation("org.assertj:assertj-core:3.26.3") diff --git a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts index c3b9ea86c8ff..1baf09fdaae1 100644 --- a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts @@ -434,7 +434,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.17.0" + toolVersion = "10.18.1" maxWarnings = 0 } diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index a607a6b60c5a..519458faf39f 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -8,8 +8,8 @@ val dependencyVersions = hashMapOf() rootProject.extra["versions"] = dependencyVersions // this line is managed by .github/scripts/update-sdk-version.sh -val otelSdkVersion = "1.40.0" -val otelContribVersion = "1.37.0-alpha" +val otelSdkVersion = "1.41.0" +val otelContribVersion = "1.38.0-alpha" val otelSdkAlphaVersion = otelSdkVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1") // Need both BOM and groovy jars @@ -29,19 +29,19 @@ val groovyVersion = "4.0.22" val DEPENDENCY_BOMS = listOf( "com.fasterxml.jackson:jackson-bom:2.17.2", "com.squareup.okio:okio-bom:3.9.0", // see https://github.com/open-telemetry/opentelemetry-java/issues/5637 - "com.google.guava:guava-bom:33.2.1-jre", + "com.google.guava:guava-bom:33.3.0-jre", "org.apache.groovy:groovy-bom:${groovyVersion}", "io.opentelemetry:opentelemetry-bom:${otelSdkVersion}", "io.opentelemetry:opentelemetry-bom-alpha:${otelSdkAlphaVersion}", - "org.junit:junit-bom:5.10.3", + "org.junit:junit-bom:5.11.0", "org.testcontainers:testcontainers-bom:1.20.1", "org.spockframework:spock-bom:2.4-M4-groovy-4.0" ) val autoServiceVersion = "1.1.1" val autoValueVersion = "1.11.0" -val errorProneVersion = "2.30.0" -val byteBuddyVersion = "1.14.18" +val errorProneVersion = "2.31.0" +val byteBuddyVersion = "1.15.1" val asmVersion = "9.7" val jmhVersion = "1.37" val mockitoVersion = "4.11.0" @@ -87,16 +87,16 @@ val DEPENDENCIES = listOf( "com.github.stefanbirkner:system-lambda:1.2.1", "com.github.stefanbirkner:system-rules:1.19.0", "uk.org.webcompere:system-stubs-jupiter:2.0.3", - "com.uber.nullaway:nullaway:0.11.1", + "com.uber.nullaway:nullaway:0.11.2", "commons-beanutils:commons-beanutils:1.9.4", - "commons-cli:commons-cli:1.8.0", + "commons-cli:commons-cli:1.9.0", "commons-codec:commons-codec:1.17.1", "commons-collections:commons-collections:3.2.2", "commons-digester:commons-digester:2.1", "commons-fileupload:commons-fileupload:1.5", "commons-io:commons-io:2.16.1", "commons-lang:commons-lang:2.6", - "commons-logging:commons-logging:1.3.3", + "commons-logging:commons-logging:1.3.4", "commons-validator:commons-validator:1.9.0", "io.netty:netty:3.10.6.Final", "io.opentelemetry.contrib:opentelemetry-aws-resources:${otelContribVersion}", diff --git a/docs/agent-features.md b/docs/agent-features.md index 9548c58d8047..22ad81e25be9 100644 --- a/docs/agent-features.md +++ b/docs/agent-features.md @@ -30,7 +30,7 @@ provides. - Can set different defaults for properties - Can customize tracer configuration programmatically - Can provide custom exporter, propagator, sampler - - Can hook into bytebuddy to customize bytecode manipulation + - Can hook into ByteBuddy to customize bytecode manipulation - Noteworthy instrumentation - Log injection of IDs (logback, log4j2, log4j) - Automatic context propagation across `Executor`s diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt index 9711244d9a5a..96e3e62e1bc3 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-instrumentation-annotations-2.7.0-SNAPSHOT.jar against opentelemetry-instrumentation-annotations-2.6.0.jar +Comparing source compatibility of opentelemetry-instrumentation-annotations-2.8.0-SNAPSHOT.jar against opentelemetry-instrumentation-annotations-2.7.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt index e6f29db1607f..c781552c9423 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-instrumentation-api-2.7.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.6.0.jar +Comparing source compatibility of opentelemetry-instrumentation-api-2.8.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.7.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt b/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt index f0024ddc916a..b7b92ce09842 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.7.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.6.0.jar +Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.8.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.7.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-starter.txt b/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-starter.txt index 32590bb0c2c9..c9acd1d6034d 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-starter.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-starter.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-spring-boot-starter-2.7.0-SNAPSHOT.jar against opentelemetry-spring-boot-starter-2.6.0.jar +Comparing source compatibility of opentelemetry-spring-boot-starter-2.8.0-SNAPSHOT.jar against opentelemetry-spring-boot-starter-2.7.0.jar No changes. \ No newline at end of file diff --git a/docs/contributing/style-guideline.md b/docs/contributing/style-guideline.md index baa025d54c57..42930cc21d25 100644 --- a/docs/contributing/style-guideline.md +++ b/docs/contributing/style-guideline.md @@ -67,6 +67,11 @@ rough guideline of what are commonly accepted static imports: - Singleton instances (especially where clearly named and hopefully immutable) - `tracer()` methods that expose tracer singleton instances +Some of these are enforced by checkstyle rules: + +- look for `RegexpSinglelineJava` in `checkstyle.xml` +- use `@SuppressWarnings("checkstyle:RegexpSinglelineJava")` to suppress the checkstyle warning + ## Ordering of class contents The following order is preferred: diff --git a/docs/contributing/writing-instrumentation-module.md b/docs/contributing/writing-instrumentation-module.md index 8fc336989187..550fd6d69de6 100644 --- a/docs/contributing/writing-instrumentation-module.md +++ b/docs/contributing/writing-instrumentation-module.md @@ -360,3 +360,156 @@ For example: ``` [suppress]: https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/#suppressing-specific-auto-instrumentation + +## Use non-inlined advice code with `invokedynamic` + +Using non-inlined advice code is possible thanks to the `invokedynamic` instruction, this strategy +is referred as "indy" in reference to this. By extension "indy modules" are the instrumentation +modules using this instrumentation strategy. + +The most common way to instrument code with ByteBuddy relies on inlining, this strategy will be +referred as "inlined" strategy as opposed to "indy". + +For inlined advices, the advice code is directly copied into the instrumented method. +In addition, all helper classes are injected into the classloader of the instrumented classes. + +For indy, advice classes are not inlined. Instead, they are loaded alongside all helper classes +into a special `InstrumentationModuleClassloader`, which sees the classes from both the instrumented +application classloader and the agent classloader. +The instrumented classes call the advice classes residing in the `InstrumentationModuleClassloader` via +invokedynamic bytecode instructions. + +Using indy instrumentation has these advantages: + +- allows instrumentations to have breakpoints set in them and be debugged using standard debugging techniques +- provides clean isolation of instrumentation advice from the application and other instrumentations +- allows advice classes to contain static fields and methods which can be accessed from the advice entry points - in fact generally good development practices are enabled (whereas inlined advices are [restricted in how they can be implemented](#use-advice-classes-to-write-code-that-will-get-injected-to-the-instrumented-library-classes)) + +### Indy modules and transition + +Making an instrumentation "indy" compatible (or native "indy") is not as straightforward as making it "inlined". +However, ByteBuddy provides a set of tools and APIs that are mentioned below to make the process as smooth as possible. + +Due to the changes needed on most of the instrumentation modules the migration can't be achieved in a single step, +we thus have to implement it in two steps: + +- `InstrumentationModule#isIndyModule` implementation return `true` (and changes needed to make it indy compatible) +- set `inlined = false` on advice methods annotated with `@Advice.OnMethodEnter` or `@Advice.OnMethodExit` + +The `otel.javaagent.experimental.indy` (default `false`) configuration option allows to opt-in for +using "indy". When set to `true`, the `io.opentelemetry.javaagent.tooling.instrumentation.indy.AdviceTransformer` +will transform advices automatically to make them "indy native". Using this option is temporary and will +be removed once all the instrumentations are "indy native". + +This configuration is automatically enabled in CI with `testIndy*` checks or when the `-PtestIndy=true` parameter is added to gradle. + +In order to preserve compatibility with both instrumentation strategies, we have to omit the `inlined = false` +from the advice method annotations. + +We have three sets of instrumentation modules: +- "inlined only": only compatible with "inlined", `isIndyModule` returns `false`. +- "indy compatible": compatible with both "indy" and "inlined", do not override `isIndyModule`, advices are modified with `AdviceTransformer` to be made "indy native" or "inlined" at runtime. +- "indy native": only compatible with "indy" `isIndyModule` returns `true`. + +The first step of the migration is to move all the "inlined only" to the "indy compatible" category +by refactoring them with the limitations described below. + +Once everything is "indy compatible", we can evaluate changing the default value of `otel.javaagent.experimental.indy` +to `true` and make it non-experimental. + +### Shared classes and common classloader + +By default, all the advices of an instrumentation module will be loaded into isolated classloaders, +one per instrumentation module. Some instrumentations require to use a common classloader in order +to preserve the semantics of `static` fields and to share classes. + +In order to load multiple `InstrumentationModule` implementations in the same classloader, you need to +override the `ExperimentalInstrumentationModule#getModuleGroup` to return an identical value. + +### Classes injected in application classloader + +Injecting classes in the application classloader is possible by implementing the +`ExperimentalInstrumentationModule#injectedClassNames` method. All the class names listed by the +returned value will be loaded in the application classloader instead of the agent or instrumentation +module classloader. + +This allows for example to access package-private methods that would not be accessible otherwise. + +### Advice local variables + +With inlined advices, declaring an advice method argument with `@Advice.Local` allows defining +a variable that is local to the advice execution for communication between the enter and exit advices. + +When advices are not inlined, usage of `@Advice.Local` is not possible. It is however possible to +return a value from the enter advice and get the value in the exit advice with a parameter annotated +with `@Advice.Enter`, for example: + +```java +@Advice.OnMethodEnter(suppress = Throwable.class, inlined = false) +public static Object onEnter(@Advice.Argument(1) Object request) { + return "enterValue"; +} + +@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inlined = false) +public static void onExit(@Advice.Argument(1) Object request, + @Advice.Enter Object enterValue) { + // do something with enterValue +} +``` + +### Modifying method arguments + +With inlined advices, using the `@Advice.Argument` annotation on method parameter with `readOnly = false` +allows modifying instrumented method arguments. + +When using non-inlined advices, reading the argument values is still done with `@Advice.Argument` +annotated parameters, however modifying the values is done through the advice method return value +and `@Advice.AssignReturned.ToArguments` annotation: + +```java +@Advice.OnMethodEnter(suppress = Throwable.class, inlined = false) +@Advice.AssignReturned.ToArguments(@ToArgument(1)) +public static Object onEnter(@Advice.Argument(1) Object request) { + return "hello"; +} +``` + +It is possible to modify multiple arguments at once by using an array, see usages of +`@Advice.AssignReturned.ToArguments` for detailed examples. + +### Modifying method return value + +With inlined advices, using the `@Advice.Return` annotation on method parameter with `readOnly = false` +allows modifying instrumented method return value on exit advice. + +When using non-inlined advices, reading the original return value is still done with the `@Advice.Return` +annotated parameter, however modifying the value is done through the advice method return value +and `@Advice.AssignReturned.ToReturned`. + +```java +@Advice.OnMethodExit(suppress = Throwable.class, inlined = false) +@Advice.AssignReturned.ToReturned +public static Object onExit(@Advice.Return Object returnValue) { + return "hello"; +} +``` + +### Writing to internal class fields + +With inlined advices, using the `@Advice.FieldValue(value = "fieldName", readOnly = false)` annotation +on advice method parameters allows modifying the `fieldName` field of the instrumented class. + +When using non-inlined advices, reading the original field value is still done with the `@Advice.FieldValue` +annotated parameter, however modifying the value is done through the advice method return value +and `@Advice.AssignReturned.ToFields` annotation. + +```java +@Advice.OnMethodEnter(suppress = Throwable.class, inlined = false) +@Advice.AssignReturned.ToFields(@ToField("fieldName")) +public static Object onEnter(@Advice.FieldValue("fieldName") Object originalFieldValue) { + return "newFieldValue"; +} +``` + +It is possible to modify multiple fields at once by using an array, see usages of +`@Advice.AssignReturned.ToFields` for detailed examples. diff --git a/examples/distro/build.gradle b/examples/distro/build.gradle index c1539ab6cb71..4d57ca72b963 100644 --- a/examples/distro/build.gradle +++ b/examples/distro/build.gradle @@ -14,7 +14,7 @@ buildscript { dependencies { classpath "com.diffplug.spotless:spotless-plugin-gradle:6.25.0" classpath "com.gradleup.shadow:shadow-gradle-plugin:8.3.0" - classpath "io.opentelemetry.instrumentation:gradle-plugins:2.7.0-alpha-SNAPSHOT" + classpath "io.opentelemetry.instrumentation:gradle-plugins:2.8.0-alpha-SNAPSHOT" } } @@ -27,14 +27,14 @@ subprojects { ext { versions = [ // this line is managed by .github/scripts/update-sdk-version.sh - opentelemetrySdk : "1.40.0", + opentelemetrySdk : "1.41.0", // these lines are managed by .github/scripts/update-version.sh - opentelemetryJavaagent : "2.7.0-SNAPSHOT", - opentelemetryJavaagentAlpha: "2.7.0-alpha-SNAPSHOT", + opentelemetryJavaagent : "2.8.0-SNAPSHOT", + opentelemetryJavaagentAlpha: "2.8.0-alpha-SNAPSHOT", autoservice : "1.1.1", - junit : "5.10.3" + junit : "5.11.0" ] deps = [ @@ -68,7 +68,7 @@ subprojects { implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:${versions.opentelemetryJavaagent}")) implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha:${versions.opentelemetryJavaagentAlpha}")) - testImplementation("org.mockito:mockito-core:5.12.0") + testImplementation("org.mockito:mockito-core:5.13.0") testImplementation(enforcedPlatform("org.junit:junit-bom:${versions.junit}")) testImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}") diff --git a/examples/distro/gradle/wrapper/gradle-wrapper.jar b/examples/distro/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c..a4b76b9530d6 100644 Binary files a/examples/distro/gradle/wrapper/gradle-wrapper.jar and b/examples/distro/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/distro/gradle/wrapper/gradle-wrapper.properties b/examples/distro/gradle/wrapper/gradle-wrapper.properties index 68e8816d71c9..2b189974c29a 100644 --- a/examples/distro/gradle/wrapper/gradle-wrapper.properties +++ b/examples/distro/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/distro/smoke-tests/build.gradle b/examples/distro/smoke-tests/build.gradle index 2db60894f48c..1e8e049c4b57 100644 --- a/examples/distro/smoke-tests/build.gradle +++ b/examples/distro/smoke-tests/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.3.2-alpha") testImplementation("io.opentelemetry:opentelemetry-api") - testImplementation("ch.qos.logback:logback-classic:1.5.6") + testImplementation("ch.qos.logback:logback-classic:1.5.7") } tasks.test { diff --git a/examples/extension/build.gradle b/examples/extension/build.gradle index 625bb443376d..233155b5bd5e 100644 --- a/examples/extension/build.gradle +++ b/examples/extension/build.gradle @@ -13,8 +13,8 @@ plugins { id "com.gradleup.shadow" version "8.3.0" id "com.diffplug.spotless" version "6.25.0" - id "io.opentelemetry.instrumentation.muzzle-generation" version "2.7.0-alpha-SNAPSHOT" - id "io.opentelemetry.instrumentation.muzzle-check" version "2.7.0-alpha-SNAPSHOT" + id "io.opentelemetry.instrumentation.muzzle-generation" version "2.8.0-alpha-SNAPSHOT" + id "io.opentelemetry.instrumentation.muzzle-check" version "2.8.0-alpha-SNAPSHOT" } group 'io.opentelemetry.example' @@ -23,13 +23,13 @@ version '1.0' ext { versions = [ // this line is managed by .github/scripts/update-sdk-version.sh - opentelemetrySdk : "1.40.0", + opentelemetrySdk : "1.41.0", // these lines are managed by .github/scripts/update-version.sh - opentelemetryJavaagent : "2.7.0-SNAPSHOT", - opentelemetryJavaagentAlpha: "2.7.0-alpha-SNAPSHOT", + opentelemetryJavaagent : "2.8.0-SNAPSHOT", + opentelemetryJavaagentAlpha: "2.8.0-alpha-SNAPSHOT", - junit : "5.10.3" + junit : "5.11.0" ] deps = [ @@ -96,7 +96,7 @@ dependencies { Only dependencies added to `implementation` configuration will be picked up by Shadow plugin and added to the resulting jar for our extension's distribution. */ - implementation 'org.apache.commons:commons-lang3:3.16.0' + implementation 'org.apache.commons:commons-lang3:3.17.0' //All dependencies below are only for tests testImplementation("org.testcontainers:testcontainers:1.20.1") @@ -108,7 +108,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}") - testRuntimeOnly("ch.qos.logback:logback-classic:1.5.6") + testRuntimeOnly("ch.qos.logback:logback-classic:1.5.7") //Otel Java instrumentation that we use and extend during integration tests otel("io.opentelemetry.javaagent:opentelemetry-javaagent:${versions.opentelemetryJavaagent}") diff --git a/examples/extension/gradle/wrapper/gradle-wrapper.jar b/examples/extension/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c..a4b76b9530d6 100644 Binary files a/examples/extension/gradle/wrapper/gradle-wrapper.jar and b/examples/extension/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/extension/gradle/wrapper/gradle-wrapper.properties b/examples/extension/gradle/wrapper/gradle-wrapper.properties index 68e8816d71c9..2b189974c29a 100644 --- a/examples/extension/gradle/wrapper/gradle-wrapper.properties +++ b/examples/extension/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradle-plugins/build.gradle.kts b/gradle-plugins/build.gradle.kts index 01cc58d93509..2264af93d904 100644 --- a/gradle-plugins/build.gradle.kts +++ b/gradle-plugins/build.gradle.kts @@ -24,11 +24,11 @@ configurations.named("compileOnly") { extendsFrom(bbGradlePlugin) } -val byteBuddyVersion = "1.14.18" +val byteBuddyVersion = "1.15.1" val aetherVersion = "1.1.0" dependencies { - implementation("com.google.guava:guava:33.2.1-jre") + implementation("com.google.guava:guava:33.3.0-jre") // we need to use byte buddy variant that does not shade asm implementation("net.bytebuddy:byte-buddy-gradle-plugin:${byteBuddyVersion}") { exclude(group = "net.bytebuddy", module = "byte-buddy") @@ -43,7 +43,7 @@ dependencies { testImplementation("org.assertj:assertj-core:3.26.3") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.10.3")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.11.0")) testImplementation("org.junit.jupiter:junit-jupiter-api") testImplementation("org.junit.jupiter:junit-jupiter-params") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") diff --git a/gradle-plugins/gradle/wrapper/gradle-wrapper.jar b/gradle-plugins/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c..a4b76b9530d6 100644 Binary files a/gradle-plugins/gradle/wrapper/gradle-wrapper.jar and b/gradle-plugins/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties index 68e8816d71c9..2b189974c29a 100644 --- a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties +++ b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradle-plugins/settings.gradle.kts b/gradle-plugins/settings.gradle.kts index 44a7733de2bb..7772973bf0f5 100644 --- a/gradle-plugins/settings.gradle.kts +++ b/gradle-plugins/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { plugins { - id("com.gradle.plugin-publish") version "1.2.1" + id("com.gradle.plugin-publish") version "1.2.2" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c..a4b76b9530d6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 68e8816d71c9..2b189974c29a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpClientInstrumenterBuilder.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpClientInstrumenterBuilder.java index 50f73aa48069..72ce1b77a701 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpClientInstrumenterBuilder.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpClientInstrumenterBuilder.java @@ -7,6 +7,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.context.propagation.TextMapSetter; import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics; @@ -18,6 +19,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter; @@ -39,18 +41,25 @@ */ public final class DefaultHttpClientInstrumenterBuilder { + // copied from PeerIncubatingAttributes + private static final AttributeKey PEER_SERVICE = AttributeKey.stringKey("peer.service"); + private final String instrumentationName; private final OpenTelemetry openTelemetry; private final List> additionalExtractors = new ArrayList<>(); + private Function< + SpanStatusExtractor, + ? extends SpanStatusExtractor> + statusExtractorTransformer = Function.identity(); private final HttpClientAttributesExtractorBuilder httpAttributesExtractorBuilder; private final HttpClientAttributesGetter attributesGetter; private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder; @Nullable private TextMapSetter headerSetter; - private Function, ? extends SpanNameExtractor> + private Function, ? extends SpanNameExtractor> spanNameExtractorTransformer = Function.identity(); private boolean emitExperimentalHttpClientMetrics = false; private Consumer> builderCustomizer = b -> {}; @@ -77,6 +86,16 @@ public DefaultHttpClientInstrumenterBuilder addAttributeExtra return this; } + @CanIgnoreReturnValue + public DefaultHttpClientInstrumenterBuilder setStatusExtractor( + Function< + SpanStatusExtractor, + ? extends SpanStatusExtractor> + statusExtractor) { + this.statusExtractorTransformer = statusExtractor; + return this; + } + /** * Configures the HTTP request headers that will be captured as span attributes. * @@ -145,7 +164,7 @@ public DefaultHttpClientInstrumenterBuilder setHeaderSetter( /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public DefaultHttpClientInstrumenterBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function, ? extends SpanNameExtractor> spanNameExtractorTransformer) { this.spanNameExtractorTransformer = spanNameExtractorTransformer; return this; @@ -159,6 +178,13 @@ public DefaultHttpClientInstrumenterBuilder setPeerServiceRes HttpClientPeerServiceAttributesExtractor.create(attributesGetter, peerServiceResolver)); } + /** Sets the {@code peer.service} attribute for http client spans. */ + @CanIgnoreReturnValue + public DefaultHttpClientInstrumenterBuilder setPeerService( + String peerService) { + return addAttributeExtractor(AttributesExtractor.constant(PEER_SERVICE, peerService)); + } + @CanIgnoreReturnValue public DefaultHttpClientInstrumenterBuilder setBuilderCustomizer( Consumer> builderCustomizer) { @@ -174,7 +200,8 @@ public Instrumenter build() { InstrumenterBuilder builder = Instrumenter.builder( openTelemetry, instrumentationName, spanNameExtractor) - .setSpanStatusExtractor(HttpSpanStatusExtractor.create(attributesGetter)) + .setSpanStatusExtractor( + statusExtractorTransformer.apply(HttpSpanStatusExtractor.create(attributesGetter))) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpClientMetrics.get()); diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java index b229a7126e48..b3c7976f6c8a 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java @@ -54,7 +54,7 @@ public final class DefaultHttpServerInstrumenterBuilder { private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder; @Nullable private TextMapGetter headerGetter; - private Function, ? extends SpanNameExtractor> + private Function, ? extends SpanNameExtractor> spanNameExtractorTransformer = Function.identity(); private final HttpServerRouteBuilder httpServerRouteBuilder; private final HttpServerAttributesGetter attributesGetter; @@ -162,7 +162,7 @@ public DefaultHttpServerInstrumenterBuilder setHeaderGetter( /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public DefaultHttpServerInstrumenterBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function, ? extends SpanNameExtractor> spanNameExtractorTransformer) { this.spanNameExtractorTransformer = spanNameExtractorTransformer; return this; diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractor.java index f49d267f7989..bdf5ab879786 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractor.java @@ -36,6 +36,8 @@ public final class MessagingAttributesExtractor AttributeKey.booleanKey("messaging.destination.anonymous"); private static final AttributeKey MESSAGING_DESTINATION_NAME = AttributeKey.stringKey("messaging.destination.name"); + private static final AttributeKey MESSAGING_DESTINATION_PARTITION_ID = + AttributeKey.stringKey("messaging.destination.partition.id"); private static final AttributeKey MESSAGING_DESTINATION_TEMPLATE = AttributeKey.stringKey("messaging.destination.template"); private static final AttributeKey MESSAGING_DESTINATION_TEMPORARY = @@ -98,6 +100,8 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST internalSet( attributes, MESSAGING_DESTINATION_TEMPLATE, getter.getDestinationTemplate(request)); } + internalSet( + attributes, MESSAGING_DESTINATION_PARTITION_ID, getter.getDestinationPartitionId(request)); boolean isAnonymousDestination = getter.isAnonymousDestination(request); if (isAnonymousDestination) { internalSet(attributes, MESSAGING_DESTINATION_ANONYMOUS, true); diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesGetter.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesGetter.java index 263bab6cc8e4..524e36b236c1 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesGetter.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesGetter.java @@ -62,6 +62,11 @@ default Long getMessagePayloadCompressedSize(REQUEST request) { @Nullable Long getBatchMessageCount(REQUEST request, @Nullable RESPONSE response); + @Nullable + default String getDestinationPartitionId(REQUEST request) { + return null; + } + /** * Extracts all values of header named {@code name} from the request, or an empty list if there * were none. diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingConsumerMetrics.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingConsumerMetrics.java new file mode 100644 index 000000000000..fec179dc8da8 --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingConsumerMetrics.java @@ -0,0 +1,112 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.semconv.messaging; + +import static java.util.logging.Level.FINE; + +import com.google.auto.value.AutoValue; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.DoubleHistogramBuilder; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongCounterBuilder; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; +import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; +import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +/** + * {@link OperationListener} which keeps track of Consumer + * metrics. + */ +public final class MessagingConsumerMetrics implements OperationListener { + private static final double NANOS_PER_S = TimeUnit.SECONDS.toNanos(1); + + // copied from MessagingIncubatingAttributes + private static final AttributeKey MESSAGING_BATCH_MESSAGE_COUNT = + AttributeKey.longKey("messaging.batch.message_count"); + private static final ContextKey MESSAGING_CONSUMER_METRICS_STATE = + ContextKey.named("messaging-consumer-metrics-state"); + private static final Logger logger = Logger.getLogger(MessagingConsumerMetrics.class.getName()); + + private final DoubleHistogram receiveDurationHistogram; + private final LongCounter receiveMessageCount; + + private MessagingConsumerMetrics(Meter meter) { + DoubleHistogramBuilder durationBuilder = + meter + .histogramBuilder("messaging.receive.duration") + .setDescription("Measures the duration of receive operation.") + .setExplicitBucketBoundariesAdvice(MessagingMetricsAdvice.DURATION_SECONDS_BUCKETS) + .setUnit("s"); + MessagingMetricsAdvice.applyReceiveDurationAdvice(durationBuilder); + receiveDurationHistogram = durationBuilder.build(); + + LongCounterBuilder longCounterBuilder = + meter + .counterBuilder("messaging.receive.messages") + .setDescription("Measures the number of received messages.") + .setUnit("{message}"); + MessagingMetricsAdvice.applyReceiveMessagesAdvice(longCounterBuilder); + receiveMessageCount = longCounterBuilder.build(); + } + + public static OperationMetrics get() { + return OperationMetricsUtil.create("messaging consumer", MessagingConsumerMetrics::new); + } + + @Override + @CanIgnoreReturnValue + public Context onStart(Context context, Attributes startAttributes, long startNanos) { + return context.with( + MESSAGING_CONSUMER_METRICS_STATE, + new AutoValue_MessagingConsumerMetrics_State(startAttributes, startNanos)); + } + + @Override + public void onEnd(Context context, Attributes endAttributes, long endNanos) { + MessagingConsumerMetrics.State state = context.get(MESSAGING_CONSUMER_METRICS_STATE); + if (state == null) { + logger.log( + FINE, + "No state present when ending context {0}. Cannot record consumer receive metrics.", + context); + return; + } + + Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build(); + receiveDurationHistogram.record( + (endNanos - state.startTimeNanos()) / NANOS_PER_S, attributes, context); + + long receiveMessagesCount = getReceiveMessagesCount(state.startAttributes(), endAttributes); + receiveMessageCount.add(receiveMessagesCount, attributes, context); + } + + private static long getReceiveMessagesCount(Attributes... attributesList) { + for (Attributes attributes : attributesList) { + Long value = attributes.get(MESSAGING_BATCH_MESSAGE_COUNT); + if (value != null) { + return value; + } + } + return 1; + } + + @AutoValue + abstract static class State { + + abstract Attributes startAttributes(); + + abstract long startTimeNanos(); + } +} diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingMetricsAdvice.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingMetricsAdvice.java index 8ada61c1683f..f87c814d2a31 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingMetricsAdvice.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingMetricsAdvice.java @@ -10,7 +10,9 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogramBuilder; +import io.opentelemetry.api.incubator.metrics.ExtendedLongCounterBuilder; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; +import io.opentelemetry.api.metrics.LongCounterBuilder; import io.opentelemetry.semconv.ErrorAttributes; import io.opentelemetry.semconv.ServerAttributes; import java.util.List; @@ -27,23 +29,41 @@ final class MessagingMetricsAdvice { AttributeKey.stringKey("messaging.destination.name"); private static final AttributeKey MESSAGING_OPERATION = AttributeKey.stringKey("messaging.operation"); - private static final AttributeKey MESSAGING_BATCH_MESSAGE_COUNT = - AttributeKey.longKey("messaging.batch.message_count"); + private static final AttributeKey MESSAGING_DESTINATION_PARTITION_ID = + AttributeKey.stringKey("messaging.destination.partition.id"); + private static final AttributeKey MESSAGING_DESTINATION_TEMPLATE = + AttributeKey.stringKey("messaging.destination.template"); + + private static final List> MESSAGING_ATTRIBUTES = + asList( + MESSAGING_SYSTEM, + MESSAGING_DESTINATION_NAME, + MESSAGING_OPERATION, + MESSAGING_DESTINATION_PARTITION_ID, + MESSAGING_DESTINATION_TEMPLATE, + ErrorAttributes.ERROR_TYPE, + ServerAttributes.SERVER_PORT, + ServerAttributes.SERVER_ADDRESS); static void applyPublishDurationAdvice(DoubleHistogramBuilder builder) { if (!(builder instanceof ExtendedDoubleHistogramBuilder)) { return; } - ((ExtendedDoubleHistogramBuilder) builder) - .setAttributesAdvice( - asList( - MESSAGING_SYSTEM, - MESSAGING_DESTINATION_NAME, - MESSAGING_OPERATION, - MESSAGING_BATCH_MESSAGE_COUNT, - ErrorAttributes.ERROR_TYPE, - ServerAttributes.SERVER_PORT, - ServerAttributes.SERVER_ADDRESS)); + ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(MESSAGING_ATTRIBUTES); + } + + static void applyReceiveDurationAdvice(DoubleHistogramBuilder builder) { + if (!(builder instanceof ExtendedDoubleHistogramBuilder)) { + return; + } + ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(MESSAGING_ATTRIBUTES); + } + + static void applyReceiveMessagesAdvice(LongCounterBuilder builder) { + if (!(builder instanceof ExtendedLongCounterBuilder)) { + return; + } + ((ExtendedLongCounterBuilder) builder).setAttributesAdvice(MESSAGING_ATTRIBUTES); } private MessagingMetricsAdvice() {} diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingProducerMetricsTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingProducerMetricsTest.java index 491387253ee9..7bf3c3670d4c 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingProducerMetricsTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingProducerMetricsTest.java @@ -5,8 +5,8 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.messaging; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; @@ -16,7 +16,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; @@ -50,7 +49,7 @@ void collectsMetrics() { Attributes responseAttributes = Attributes.builder() .put(MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID, "1:1:0:0") - .put(MessagingIncubatingAttributes.MESSAGING_BATCH_MESSAGE_COUNT, 2) + .put(MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID, "1") .build(); Context parent = @@ -76,7 +75,7 @@ void collectsMetrics() { assertThat(metricReader.collectAllMetrics()) .satisfiesExactlyInAnyOrder( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasName("messaging.publish.duration") .hasUnit("s") .hasDescription("Measures the duration of publish operation.") @@ -90,6 +89,10 @@ void collectsMetrics() { equalTo( MessagingIncubatingAttributes.MESSAGING_SYSTEM, "pulsar"), + equalTo( + MessagingIncubatingAttributes + .MESSAGING_DESTINATION_PARTITION_ID, + "1"), equalTo( MessagingIncubatingAttributes .MESSAGING_DESTINATION_NAME, @@ -108,7 +111,7 @@ void collectsMetrics() { assertThat(metricReader.collectAllMetrics()) .satisfiesExactlyInAnyOrder( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasName("messaging.publish.duration") .hasHistogramSatisfying( histogram -> diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java index 113ce294525c..44c997f85f4a 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java @@ -6,12 +6,11 @@ package io.opentelemetry.instrumentation.api.instrumenter; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.internal.InstrumentationUtil; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.SpanKey; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Map; import java.util.Set; @@ -90,20 +89,9 @@ public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { static class ByContextKey implements SpanSuppressor { private final SpanSuppressor delegate; - private final Method shouldSuppressInstrumentation; ByContextKey(SpanSuppressor delegate) { this.delegate = delegate; - Method shouldSuppressInstrumentation; - try { - Class instrumentationUtil = - Class.forName("io.opentelemetry.exporter.internal.InstrumentationUtil"); - shouldSuppressInstrumentation = - instrumentationUtil.getDeclaredMethod("shouldSuppressInstrumentation", Context.class); - } catch (ClassNotFoundException | NoSuchMethodException e) { - shouldSuppressInstrumentation = null; - } - this.shouldSuppressInstrumentation = shouldSuppressInstrumentation; } @Override @@ -113,22 +101,10 @@ public Context storeInContext(Context context, SpanKind spanKind, Span span) { @Override public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { - if (suppressByContextKey(parentContext)) { + if (InstrumentationUtil.shouldSuppressInstrumentation(parentContext)) { return true; } return delegate.shouldSuppress(parentContext, spanKind); } - - private boolean suppressByContextKey(Context context) { - if (shouldSuppressInstrumentation == null) { - return false; - } - - try { - return (boolean) shouldSuppressInstrumentation.invoke(null, context); - } catch (IllegalAccessException | InvocationTargetException e) { - return false; - } - } } } diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategyTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategyTest.java index 6ec5d802f874..82e29fdbdaa6 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategyTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategyTest.java @@ -13,10 +13,10 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.opentelemetry.api.internal.InstrumentationUtil; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; -import io.opentelemetry.exporter.internal.InstrumentationUtil; import io.opentelemetry.instrumentation.api.internal.SpanKey; import java.util.HashSet; import java.util.Set; diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/OperationMetricsUtilTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/OperationMetricsUtilTest.java index 945ab3297a1e..c7a8d0d54a2b 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/OperationMetricsUtilTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/OperationMetricsUtilTest.java @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.api.internal; +import static org.assertj.core.api.Assertions.assertThat; + import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterProvider; @@ -13,7 +15,6 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.lang.reflect.Proxy; import java.util.concurrent.atomic.AtomicBoolean; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -29,7 +30,7 @@ void noWarning() { "test metrics", meter -> null, (s, doubleHistogramBuilder) -> warning.set(true)); operationMetrics.create(testing.getOpenTelemetry().getMeter("test")); - Assertions.assertThat(warning).isFalse(); + assertThat(warning).isFalse(); } @Test @@ -40,7 +41,7 @@ void noWarningWithNoopMetrics() { "test metrics", meter -> null, (s, doubleHistogramBuilder) -> warning.set(true)); operationMetrics.create(MeterProvider.noop().get("test")); - Assertions.assertThat(warning).isFalse(); + assertThat(warning).isFalse(); } @Test @@ -65,7 +66,7 @@ void warning() { }); operationMetrics.create(meter); - Assertions.assertThat(warning).isTrue(); + assertThat(warning).isTrue(); } private static DoubleHistogramBuilder proxyDoubleHistogramBuilder(Meter meter) { diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy b/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy deleted file mode 100644 index 36f81f58b828..000000000000 --- a/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class DubboTest extends AbstractDubboTest implements AgentTestTrait { -} diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy b/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy deleted file mode 100644 index 432be0305633..000000000000 --- a/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class DubboTraceChainTest extends AbstractDubboTraceChainTest implements AgentTestTrait { -} diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/DubboAgentTest.java b/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/DubboAgentTest.java new file mode 100644 index 000000000000..1a7e2fb42405 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/DubboAgentTest.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.apachedubbo.v2_7; + +import io.opentelemetry.instrumentation.apachedubbo.v2_7.AbstractDubboTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DubboAgentTest extends AbstractDubboTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/DubboAgentTraceChainTest.java b/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/DubboAgentTraceChainTest.java new file mode 100644 index 000000000000..bf34ae8633fe --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/javaagent/src/testDubbo/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/DubboAgentTraceChainTest.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.apachedubbo.v2_7; + +import io.opentelemetry.instrumentation.apachedubbo.v2_7.AbstractDubboTraceChainTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DubboAgentTraceChainTest extends AbstractDubboTraceChainTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersGetter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersGetter.java index 2b8e262bf7b0..eef5238bb46d 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersGetter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersGetter.java @@ -11,11 +11,13 @@ enum DubboHeadersGetter implements TextMapGetter { INSTANCE; @Override + @SuppressWarnings("deprecation") // deprecation for dubbo 3.2.15 public Iterable keys(DubboRequest request) { return request.invocation().getAttachments().keySet(); } @Override + @SuppressWarnings("deprecation") // deprecation for dubbo 3.2.15 public String get(DubboRequest request, String key) { return request.invocation().getAttachment(key); } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java index a19e8ef6d5db..d3d239096bb1 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java @@ -29,6 +29,7 @@ final class TracingFilter implements Filter { } @Override + @SuppressWarnings("deprecation") // deprecation for RpcContext.getContext() public Result invoke(Invoker invoker, Invocation invocation) { if (!(invocation instanceof RpcInvocation)) { return invoker.invoke(invocation); diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy deleted file mode 100644 index daa71bc333c6..000000000000 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import io.opentelemetry.instrumentation.test.LibraryTestTrait - -class DubboTest extends AbstractDubboTest implements LibraryTestTrait { -} diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy deleted file mode 100644 index ee0b1040dec5..000000000000 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import io.opentelemetry.instrumentation.test.LibraryTestTrait - -class DubboTraceChainTest extends AbstractDubboTraceChainTest implements LibraryTestTrait { -} diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.java new file mode 100644 index 000000000000..84667a51e074 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DubboTest extends AbstractDubboTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.java new file mode 100644 index 000000000000..dab4e6f03150 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DubboTraceChainTest extends AbstractDubboTraceChainTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy deleted file mode 100644 index 6cd2d132a41d..000000000000 --- a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService -import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.instrumentation.test.utils.PortUtils -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.NetworkAttributes -import org.apache.dubbo.common.utils.NetUtils -import org.apache.dubbo.config.ApplicationConfig -import org.apache.dubbo.config.ProtocolConfig -import org.apache.dubbo.config.ReferenceConfig -import org.apache.dubbo.config.RegistryConfig -import org.apache.dubbo.config.ServiceConfig -import org.apache.dubbo.config.bootstrap.DubboBootstrap -import org.apache.dubbo.rpc.service.GenericService -import spock.lang.Shared -import spock.lang.Unroll - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.instrumentation.apachedubbo.v2_7.DubboTestUtil.newDubboBootstrap -import static io.opentelemetry.instrumentation.apachedubbo.v2_7.DubboTestUtil.newFrameworkModel - -@Unroll -abstract class AbstractDubboTest extends InstrumentationSpecification { - - @Shared - def protocolConfig = new ProtocolConfig() - - def setupSpec() { - NetUtils.LOCAL_ADDRESS = InetAddress.getLoopbackAddress() - } - - ReferenceConfig configureClient(int port) { - ReferenceConfig reference = new ReferenceConfig<>() - reference.setInterface(HelloService) - reference.setGeneric("true") - reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000") - return reference - } - - ServiceConfig configureServer() { - def registerConfig = new RegistryConfig() - registerConfig.setAddress("N/A") - ServiceConfig service = new ServiceConfig<>() - service.setInterface(HelloService) - service.setRef(new HelloServiceImpl()) - service.setRegistry(registerConfig) - return service - } - - def "test apache dubbo base #dubbo"() { - setup: - def port = PortUtils.findOpenPort() - protocolConfig.setPort(port) - - def frameworkModel = newFrameworkModel() - DubboBootstrap bootstrap = newDubboBootstrap(frameworkModel) - bootstrap.application(new ApplicationConfig("dubbo-test-provider")) - .service(configureServer()) - .protocol(protocolConfig) - .start() - - def consumerProtocolConfig = new ProtocolConfig() - consumerProtocolConfig.setRegister(false) - - def reference = configureClient(port) - DubboBootstrap consumerBootstrap = newDubboBootstrap(frameworkModel) - consumerBootstrap.application(new ApplicationConfig("dubbo-demo-api-consumer")) - .reference(reference) - .protocol(consumerProtocolConfig) - .start() - - when: - GenericService genericService = reference.get() - def o = new Object[1] - o[0] = "hello" - def response = runWithSpan("parent") { - genericService.$invoke("hello", [String.getName()] as String[], o) - } - - then: - response == "hello" - assertTraces(1) { - trace(0, 3) { - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - } - span(1) { - name "org.apache.dubbo.rpc.service.GenericService/\$invoke" - kind CLIENT - childOf span(0) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" - "$RpcIncubatingAttributes.RPC_METHOD" "\$invoke" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" Long - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == null || it instanceof String} - "$NetworkAttributes.NETWORK_PEER_PORT" { it == null || it instanceof Long} - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - span(2) { - name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello" - kind SERVER - childOf span(1) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService" - "$RpcIncubatingAttributes.RPC_METHOD" "hello" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" String - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - } - } - - cleanup: - bootstrap.destroy() - consumerBootstrap.destroy() - frameworkModel?.destroy() - } - - def "test apache dubbo test #dubbo"() { - setup: - def port = PortUtils.findOpenPort() - protocolConfig.setPort(port) - - def frameworkModel = newFrameworkModel() - DubboBootstrap bootstrap = newDubboBootstrap(frameworkModel) - bootstrap.application(new ApplicationConfig("dubbo-test-async-provider")) - .service(configureServer()) - .protocol(protocolConfig) - .start() - - def consumerProtocolConfig = new ProtocolConfig() - consumerProtocolConfig.setRegister(false) - - def reference = configureClient(port) - DubboBootstrap consumerBootstrap = newDubboBootstrap(frameworkModel) - consumerBootstrap.application(new ApplicationConfig("dubbo-demo-async-api-consumer")) - .reference(reference) - .protocol(consumerProtocolConfig) - .start() - - when: - GenericService genericService = reference.get() - def o = new Object[1] - o[0] = "hello" - def responseAsync = runWithSpan("parent") { - genericService.$invokeAsync("hello", [String.getName()] as String[], o) - } - - then: - responseAsync.get() == "hello" - assertTraces(1) { - trace(0, 3) { - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - } - span(1) { - name "org.apache.dubbo.rpc.service.GenericService/\$invokeAsync" - kind CLIENT - childOf span(0) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" - "$RpcIncubatingAttributes.RPC_METHOD" "\$invokeAsync" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" Long - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == null || it instanceof String} - "$NetworkAttributes.NETWORK_PEER_PORT" { it == null || it instanceof Long} - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - span(2) { - name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello" - kind SERVER - childOf span(1) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService" - "$RpcIncubatingAttributes.RPC_METHOD" "hello" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" String - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - } - } - - cleanup: - bootstrap.destroy() - consumerBootstrap.destroy() - frameworkModel?.destroy() - } -} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.groovy b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.groovy deleted file mode 100644 index a7e2f6150b67..000000000000 --- a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.groovy +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService -import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService -import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl -import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.MiddleServiceImpl -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.instrumentation.test.utils.PortUtils -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.NetworkAttributes -import org.apache.dubbo.common.utils.NetUtils -import org.apache.dubbo.config.ApplicationConfig -import org.apache.dubbo.config.ProtocolConfig -import org.apache.dubbo.config.ReferenceConfig -import org.apache.dubbo.config.RegistryConfig -import org.apache.dubbo.config.ServiceConfig -import org.apache.dubbo.config.bootstrap.DubboBootstrap -import org.apache.dubbo.rpc.service.GenericService -import spock.lang.Unroll - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.instrumentation.apachedubbo.v2_7.DubboTestUtil.newDubboBootstrap -import static io.opentelemetry.instrumentation.apachedubbo.v2_7.DubboTestUtil.newFrameworkModel - -@Unroll -abstract class AbstractDubboTraceChainTest extends InstrumentationSpecification { - - def setupSpec() { - NetUtils.LOCAL_ADDRESS = InetAddress.getLoopbackAddress() - } - - ReferenceConfig configureClient(int port) { - ReferenceConfig reference = new ReferenceConfig<>() - reference.setInterface(HelloService) - reference.setGeneric("true") - reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000") - return reference - } - - ReferenceConfig configureLocalClient(int port) { - ReferenceConfig reference = new ReferenceConfig<>() - reference.setInterface(HelloService) - reference.setGeneric("true") - reference.setUrl("injvm://localhost:" + port + "/?timeout=30000") - return reference - } - - ReferenceConfig configureMiddleClient(int port) { - ReferenceConfig reference = new ReferenceConfig<>() - reference.setInterface(MiddleService) - reference.setGeneric("true") - reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000") - return reference - } - - ServiceConfig configureServer() { - def registerConfig = new RegistryConfig() - registerConfig.setAddress("N/A") - ServiceConfig service = new ServiceConfig<>() - service.setInterface(HelloService) - service.setRef(new HelloServiceImpl()) - service.setRegistry(registerConfig) - return service - } - - ServiceConfig configureMiddleServer(ReferenceConfig referenceConfig) { - def registerConfig = new RegistryConfig() - registerConfig.setAddress("N/A") - ServiceConfig service = new ServiceConfig<>() - service.setInterface(MiddleService) - service.setRef(new MiddleServiceImpl(referenceConfig)) - service.setRegistry(registerConfig) - return service - } - - def "test that context is propagated correctly in chained dubbo calls"() { - setup: - def port = PortUtils.findOpenPorts(2) - def middlePort = port + 1 - def protocolConfig = new ProtocolConfig() - protocolConfig.setPort(port) - - def frameworkModel = newFrameworkModel() - DubboBootstrap bootstrap = newDubboBootstrap(frameworkModel) - bootstrap.application(new ApplicationConfig("dubbo-test-provider")) - .service(configureServer()) - .protocol(protocolConfig) - .start() - - def middleProtocolConfig = new ProtocolConfig() - middleProtocolConfig.setPort(middlePort) - - def reference = configureClient(port) - DubboBootstrap middleBootstrap = newDubboBootstrap(frameworkModel) - middleBootstrap.application(new ApplicationConfig("dubbo-demo-middle")) - .reference(reference) - .service(configureMiddleServer(reference)) - .protocol(middleProtocolConfig) - .start() - - - def consumerProtocolConfig = new ProtocolConfig() - consumerProtocolConfig.setRegister(false) - - def middleReference = configureMiddleClient(middlePort) - DubboBootstrap consumerBootstrap = newDubboBootstrap(frameworkModel) - consumerBootstrap.application(new ApplicationConfig("dubbo-demo-api-consumer")) - .reference(middleReference) - .protocol(consumerProtocolConfig) - .start() - - when: - GenericService genericService = middleReference.get() - def response = runWithSpan("parent") { - genericService.$invoke("hello", [String.getName()] as String[], ["hello"] as Object[]) - } - - then: - response == "hello" - assertTraces(1) { - trace(0, 5) { - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - } - span(1) { - name "org.apache.dubbo.rpc.service.GenericService/\$invoke" - kind CLIENT - childOf span(0) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" - "$RpcIncubatingAttributes.RPC_METHOD" "\$invoke" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" Long - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == null || it instanceof String} - "$NetworkAttributes.NETWORK_PEER_PORT" { it == null || it instanceof Long} - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - span(2) { - name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello" - kind SERVER - childOf span(1) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService" - "$RpcIncubatingAttributes.RPC_METHOD" "hello" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" String - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - span(3) { - name "org.apache.dubbo.rpc.service.GenericService/\$invoke" - kind CLIENT - childOf span(2) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" - "$RpcIncubatingAttributes.RPC_METHOD" "\$invoke" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" Long - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == null || it instanceof String } - "$NetworkAttributes.NETWORK_PEER_PORT" { it == null || it instanceof Long } - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - span(4) { - name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello" - kind SERVER - childOf span(3) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService" - "$RpcIncubatingAttributes.RPC_METHOD" "hello" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" String - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - } - } - - cleanup: - bootstrap.destroy() - middleBootstrap.destroy() - consumerBootstrap.destroy() - } - - def "test ignore injvm calls"() { - setup: - def port = PortUtils.findOpenPorts(2) - def middlePort = port + 1 - def protocolConfig = new ProtocolConfig() - protocolConfig.setPort(port) - - def frameworkModel = newFrameworkModel() - DubboBootstrap bootstrap = newDubboBootstrap(frameworkModel) - bootstrap.application(new ApplicationConfig("dubbo-test-provider")) - .service(configureServer()) - .protocol(protocolConfig) - .start() - - def middleProtocolConfig = new ProtocolConfig() - middleProtocolConfig.setPort(middlePort) - - def reference = configureLocalClient(port) - DubboBootstrap middleBootstrap = newDubboBootstrap(frameworkModel) - middleBootstrap.application(new ApplicationConfig("dubbo-demo-middle")) - .reference(reference) - .service(configureMiddleServer(reference)) - .protocol(middleProtocolConfig) - .start() - - - def consumerProtocolConfig = new ProtocolConfig() - consumerProtocolConfig.setRegister(false) - - def middleReference = configureMiddleClient(middlePort) - DubboBootstrap consumerBootstrap = newDubboBootstrap(frameworkModel) - consumerBootstrap.application(new ApplicationConfig("dubbo-demo-api-consumer")) - .reference(middleReference) - .protocol(consumerProtocolConfig) - .start() - - when: - GenericService genericService = middleReference.get() - def response = runWithSpan("parent") { - genericService.$invoke("hello", [String.getName()] as String[], ["hello"] as Object[]) - } - - then: - response == "hello" - assertTraces(1) { - trace(0, 3) { - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - } - span(1) { - name "org.apache.dubbo.rpc.service.GenericService/\$invoke" - kind CLIENT - childOf span(0) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" - "$RpcIncubatingAttributes.RPC_METHOD" "\$invoke" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" Long - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == null || it instanceof String} - "$NetworkAttributes.NETWORK_PEER_PORT" { it == null || it instanceof Long} - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - span(2) { - name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello" - kind SERVER - childOf span(1) - attributes { - "$RpcIncubatingAttributes.RPC_SYSTEM" "apache_dubbo" - "$RpcIncubatingAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService" - "$RpcIncubatingAttributes.RPC_METHOD" "hello" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" String - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - } - } - } - } - - cleanup: - bootstrap.destroy() - middleBootstrap.destroy() - consumerBootstrap.destroy() - } -} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTestUtil.groovy b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTestUtil.groovy deleted file mode 100644 index 03ec85a3955d..000000000000 --- a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTestUtil.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.apachedubbo.v2_7 - -import org.apache.dubbo.config.bootstrap.DubboBootstrap - -class DubboTestUtil { - static newFrameworkModel() { - try { - // only present in latest dep - return Class.forName("org.apache.dubbo.rpc.model.FrameworkModel").newInstance() - } catch (ClassNotFoundException exception) { - return null - } - } - - static DubboBootstrap newDubboBootstrap(Object frameworkModel) { - if (frameworkModel == null) { - return DubboBootstrap.newInstance() - } - return DubboBootstrap.newInstance(frameworkModel) - } -} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java new file mode 100644 index 000000000000..85ea385128cd --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java @@ -0,0 +1,287 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7; + +import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl; +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.semconv.NetworkAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.rpc.service.GenericService; +import org.assertj.core.api.AbstractAssert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class AbstractDubboTest { + + private final ProtocolConfig protocolConfig = new ProtocolConfig(); + + protected abstract InstrumentationExtension testing(); + + @RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create(); + + @BeforeAll + static void setUp() throws Exception { + System.setProperty("dubbo.application.qos-enable", "false"); + Field field = NetUtils.class.getDeclaredField("LOCAL_ADDRESS"); + field.setAccessible(true); + field.set(null, InetAddress.getLoopbackAddress()); + } + + @AfterAll + static void tearDown() { + System.clearProperty("dubbo.application.qos-enable"); + } + + ReferenceConfig configureClient(int port) { + ReferenceConfig reference = new ReferenceConfig<>(); + reference.setInterface(HelloService.class); + reference.setGeneric("true"); + reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000"); + return reference; + } + + ServiceConfig configureServer() { + RegistryConfig registerConfig = new RegistryConfig(); + registerConfig.setAddress("N/A"); + ServiceConfig service = new ServiceConfig<>(); + service.setInterface(HelloService.class); + service.setRef(new HelloServiceImpl()); + service.setRegistry(registerConfig); + return service; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + ReferenceConfig convertReference(ReferenceConfig config) { + return (ReferenceConfig) config; + } + + @Test + void testApacheDubboBase() throws ReflectiveOperationException { + int port = PortUtils.findOpenPort(); + protocolConfig.setPort(port); + // provider boostrap + DubboBootstrap bootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(bootstrap::destroy); + bootstrap + .application(new ApplicationConfig("dubbo-test-provider")) + .service(configureServer()) + .protocol(protocolConfig) + .start(); + + // consumer boostrap + DubboBootstrap consumerBootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(consumerBootstrap::destroy); + ReferenceConfig referenceConfig = configureClient(port); + ProtocolConfig consumerProtocolConfig = new ProtocolConfig(); + consumerProtocolConfig.setRegister(false); + consumerBootstrap + .application(new ApplicationConfig("dubbo-demo-api-consumer")) + .reference(referenceConfig) + .protocol(consumerProtocolConfig) + .start(); + + // generic call + ReferenceConfig reference = convertReference(referenceConfig); + GenericService genericService = reference.get(); + + Object response = + runWithSpan( + "parent", + () -> + genericService.$invoke( + "hello", new String[] {String.class.getName()}, new Object[] {"hello"})); + + assertThat(response).isEqualTo("hello"); + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "org.apache.dubbo.rpc.service.GenericService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "$invoke"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + satisfies( + ServerAttributes.SERVER_PORT, k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(String.class))), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(Long.class))), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))), + span -> + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "hello"), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> k.isInstanceOf(String.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, AbstractAssert::isNull)))); + } + + @Test + void testApacheDubboTest() + throws ExecutionException, InterruptedException, ReflectiveOperationException { + int port = PortUtils.findOpenPort(); + protocolConfig.setPort(port); + + DubboBootstrap bootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(bootstrap::destroy); + bootstrap + .application(new ApplicationConfig("dubbo-test-async-provider")) + .service(configureServer()) + .protocol(protocolConfig) + .start(); + + ProtocolConfig consumerProtocolConfig = new ProtocolConfig(); + consumerProtocolConfig.setRegister(false); + + ReferenceConfig referenceConfig = configureClient(port); + DubboBootstrap consumerBootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(consumerBootstrap::destroy); + consumerBootstrap + .application(new ApplicationConfig("dubbo-demo-async-api-consumer")) + .reference(referenceConfig) + .protocol(consumerProtocolConfig) + .start(); + + // generic call + ReferenceConfig reference = convertReference(referenceConfig); + GenericService genericService = reference.get(); + CompletableFuture response = + runWithSpan( + "parent", + () -> + genericService.$invokeAsync( + "hello", new String[] {String.class.getName()}, new Object[] {"hello"})); + + assertThat(response.get()).isEqualTo("hello"); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invokeAsync") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "org.apache.dubbo.rpc.service.GenericService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "$invokeAsync"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + satisfies( + ServerAttributes.SERVER_PORT, k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(String.class))), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(Long.class))), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))), + span -> + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "hello"), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> k.isInstanceOf(String.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))))); + } +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java new file mode 100644 index 000000000000..4f90694b49f8 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java @@ -0,0 +1,411 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7; + +import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.MiddleServiceImpl; +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.semconv.NetworkAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import java.lang.reflect.Field; +import java.net.InetAddress; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.rpc.service.GenericService; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class AbstractDubboTraceChainTest { + + @RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create(); + + @BeforeAll + static void setUp() throws Exception { + System.setProperty("dubbo.application.qos-enable", "false"); + Field field = NetUtils.class.getDeclaredField("LOCAL_ADDRESS"); + field.setAccessible(true); + field.set(null, InetAddress.getLoopbackAddress()); + } + + @AfterAll + static void tearDown() { + System.clearProperty("dubbo.application.qos-enable"); + } + + protected abstract InstrumentationExtension testing(); + + ReferenceConfig configureClient(int port) { + ReferenceConfig reference = new ReferenceConfig<>(); + reference.setInterface(HelloService.class); + reference.setGeneric("true"); + reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000"); + return reference; + } + + ReferenceConfig configureLocalClient(int port) { + ReferenceConfig reference = new ReferenceConfig<>(); + reference.setInterface(HelloService.class); + reference.setGeneric("true"); + reference.setUrl("injvm://localhost:" + port + "/?timeout=30000"); + return reference; + } + + ReferenceConfig configureMiddleClient(int port) { + ReferenceConfig reference = new ReferenceConfig<>(); + reference.setInterface(MiddleService.class); + reference.setGeneric("true"); + reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000"); + return reference; + } + + ServiceConfig configureServer() { + RegistryConfig registerConfig = new RegistryConfig(); + registerConfig.setAddress("N/A"); + ServiceConfig service = new ServiceConfig<>(); + service.setInterface(HelloService.class); + service.setRef(new HelloServiceImpl()); + service.setRegistry(registerConfig); + return service; + } + + ServiceConfig configureMiddleServer( + ReferenceConfig referenceConfig) { + RegistryConfig registerConfig = new RegistryConfig(); + registerConfig.setAddress("N/A"); + ServiceConfig service = new ServiceConfig<>(); + service.setInterface(MiddleService.class); + service.setRef(new MiddleServiceImpl(referenceConfig)); + service.setRegistry(registerConfig); + return service; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + ReferenceConfig convertReference(ReferenceConfig config) { + return (ReferenceConfig) config; + } + + @Test + @DisplayName("test that context is propagated correctly in chained dubbo calls") + void testDubboChain() throws ReflectiveOperationException { + int port = PortUtils.findOpenPorts(2); + int middlePort = port + 1; + + // setup hello service provider + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.setPort(port); + + DubboBootstrap bootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(bootstrap::destroy); + bootstrap + .application(new ApplicationConfig("dubbo-test-provider")) + .service(configureServer()) + .protocol(protocolConfig) + .start(); + + // setup middle service provider, hello service consumer + ProtocolConfig middleProtocolConfig = new ProtocolConfig(); + middleProtocolConfig.setPort(middlePort); + + ReferenceConfig clientReference = configureClient(port); + DubboBootstrap middleBootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(middleBootstrap::destroy); + middleBootstrap + .application(new ApplicationConfig("dubbo-demo-middle")) + .reference(clientReference) + .service(configureMiddleServer(clientReference)) + .protocol(middleProtocolConfig) + .start(); + + // setup middle service consumer + ProtocolConfig consumerProtocolConfig = new ProtocolConfig(); + consumerProtocolConfig.setRegister(false); + + ReferenceConfig middleReference = configureMiddleClient(middlePort); + DubboBootstrap consumerBootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(consumerBootstrap::destroy); + consumerBootstrap + .application(new ApplicationConfig("dubbo-demo-api-consumer")) + .reference(middleReference) + .protocol(consumerProtocolConfig) + .start(); + + GenericService genericService = convertReference(middleReference).get(); + + Object response = + runWithSpan( + "parent", + () -> + genericService.$invoke( + "hello", new String[] {String.class.getName()}, new Object[] {"hello"})); + + assertThat(response).isEqualTo("hello"); + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "org.apache.dubbo.rpc.service.GenericService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "$invoke"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + satisfies( + ServerAttributes.SERVER_PORT, k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(String.class))), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(Long.class))), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))), + span -> + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "hello"), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> k.isInstanceOf(String.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))), + span -> + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(2)) + .hasAttributesSatisfyingExactly( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "org.apache.dubbo.rpc.service.GenericService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "$invoke"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + satisfies( + ServerAttributes.SERVER_PORT, k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(String.class))), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(Long.class))), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))), + span -> + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(3)) + .hasAttributesSatisfying( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "hello"), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> k.isInstanceOf(String.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))))); + } + + @Test + @DisplayName("test ignore injvm calls") + void testDubboChainInJvm() throws ReflectiveOperationException { + int port = PortUtils.findOpenPorts(2); + int middlePort = port + 1; + + // setup hello service provider + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.setPort(port); + + DubboBootstrap bootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(bootstrap::destroy); + bootstrap + .application(new ApplicationConfig("dubbo-test-provider")) + .service(configureServer()) + .protocol(protocolConfig) + .start(); + + // setup middle service provider, hello service consumer + ProtocolConfig middleProtocolConfig = new ProtocolConfig(); + middleProtocolConfig.setPort(middlePort); + + ReferenceConfig clientReference = configureLocalClient(port); + DubboBootstrap middleBootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(middleBootstrap::destroy); + middleBootstrap + .application(new ApplicationConfig("dubbo-demo-middle")) + .service(configureMiddleServer(clientReference)) + .protocol(middleProtocolConfig) + .start(); + + // setup middle service consumer + ProtocolConfig consumerProtocolConfig = new ProtocolConfig(); + consumerProtocolConfig.setRegister(false); + + ReferenceConfig middleReference = configureMiddleClient(middlePort); + DubboBootstrap consumerBootstrap = DubboTestUtil.newDubboBootstrap(); + cleanup.deferCleanup(consumerBootstrap::destroy); + consumerBootstrap + .application(new ApplicationConfig("dubbo-demo-api-consumer")) + .reference(middleReference) + .protocol(consumerProtocolConfig) + .start(); + + GenericService genericService = convertReference(middleReference).get(); + + Object response = + runWithSpan( + "parent", + () -> + genericService.$invoke( + "hello", new String[] {String.class.getName()}, new Object[] {"hello"})); + + assertThat(response).isEqualTo("hello"); + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "org.apache.dubbo.rpc.service.GenericService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "$invoke"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + satisfies( + ServerAttributes.SERVER_PORT, k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(String.class))), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isInstanceOf(Long.class))), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))), + span -> + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying( + equalTo( + RpcIncubatingAttributes.RPC_SYSTEM, + RpcIncubatingAttributes.RpcSystemValues.APACHE_DUBBO), + equalTo( + RpcIncubatingAttributes.RPC_SERVICE, + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService"), + equalTo(RpcIncubatingAttributes.RPC_METHOD, "hello"), + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + k -> k.isInstanceOf(String.class)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + k -> k.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, + k -> + k.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6")))))); + } +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTestUtil.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTestUtil.java new file mode 100644 index 000000000000..1de3cef738ce --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTestUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +/** compatible with dubbo3.x and dubbo 2.7 */ +class DubboTestUtil { + + private DubboTestUtil() {} + + static Object newFrameworkModel() { + try { + // only present in latest dep + return Class.forName("org.apache.dubbo.rpc.model.FrameworkModel") + .getDeclaredConstructor() + .newInstance(); + } catch (ReflectiveOperationException exception) { + return null; + } + } + + static DubboBootstrap newDubboBootstrap() throws ReflectiveOperationException { + Object newFrameworkModel = newFrameworkModel(); + if (newFrameworkModel == null) { + return newDubboBootstrapV27(); + } else { + return newDubboBootstrapV3(newFrameworkModel); + } + } + + private static DubboBootstrap newDubboBootstrapV3(Object newFrameworkModel) + throws ReflectiveOperationException { + Method getInstance = + DubboBootstrap.class.getDeclaredMethod("newInstance", newFrameworkModel.getClass()); + return (DubboBootstrap) getInstance.invoke(null, newFrameworkModel); + } + + private static DubboBootstrap newDubboBootstrapV27() throws ReflectiveOperationException { + Constructor constructor = DubboBootstrap.class.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } +} diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/AbstractCommonsHttpClientTest.groovy b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/AbstractCommonsHttpClientTest.groovy deleted file mode 100644 index 41452ae2c947..000000000000 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/AbstractCommonsHttpClientTest.groovy +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import org.apache.commons.httpclient.HttpClient -import org.apache.commons.httpclient.HttpConnectionManager -import org.apache.commons.httpclient.HttpMethod -import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager -import org.apache.commons.httpclient.methods.DeleteMethod -import org.apache.commons.httpclient.methods.GetMethod -import org.apache.commons.httpclient.methods.HeadMethod -import org.apache.commons.httpclient.methods.OptionsMethod -import org.apache.commons.httpclient.methods.PostMethod -import org.apache.commons.httpclient.methods.PutMethod -import org.apache.commons.httpclient.methods.TraceMethod -import spock.lang.Shared - -abstract class AbstractCommonsHttpClientTest extends HttpClientTest implements AgentTestTrait { - @Shared - HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager() - @Shared - HttpClient client = buildClient(false) - @Shared - HttpClient clientWithReadTimeout = buildClient(true) - - def buildClient(boolean readTimeout) { - HttpClient client = new HttpClient(connectionManager) - client.setConnectionTimeout(CONNECT_TIMEOUT_MS) - if (readTimeout) { - client.setTimeout(READ_TIMEOUT_MS) - } - return client - } - - HttpClient getClient(URI uri) { - if (uri.toString().contains("/read-timeout")) { - return clientWithReadTimeout - } - return client - } - - @Override - HttpMethod buildRequest(String method, URI uri, Map headers) { - def request - switch (method) { - case "GET": - request = new GetMethod(uri.toString()) - break - case "PUT": - request = new PutMethod(uri.toString()) - break - case "POST": - request = new PostMethod(uri.toString()) - break - case "HEAD": - request = new HeadMethod(uri.toString()) - break - case "DELETE": - request = new DeleteMethod(uri.toString()) - break - case "OPTIONS": - request = new OptionsMethod(uri.toString()) - break - case "TRACE": - request = new TraceMethod(uri.toString()) - break - default: - throw new IllegalStateException("Unsupported method: " + method) - } - headers.each { request.setRequestHeader(it.key, it.value) } - return request - } - - @Override - boolean testCircularRedirects() { - false - } - - @Override - boolean testReusedRequest() { - // apache commons throws an exception if the request is reused without being recycled first - // at which point this test is not useful (and requires re-populating uri) - false - } - - @Override - boolean testCallback() { - false - } - - @Override - boolean testNonStandardHttpMethod() { - false - } -} diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientLatestDepsTest.groovy b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientLatestDepsTest.groovy deleted file mode 100644 index 0a68757a007b..000000000000 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientLatestDepsTest.groovy +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.apache.commons.httpclient.HttpMethod -import spock.lang.IgnoreIf - -//this test will be ignored if not executed with -PtestLatestDeps=true -//because the latest dependency commons-httpclient v3.1 allows a call to the executeMethod -//with some null parameters like HttpClient.executeMethod(null, request, null) -//but this construct is not allowed in commons-httpclient v2 that is used for regular otel testing -@IgnoreIf({ !Boolean.getBoolean("testLatestDeps") }) -class CommonsHttpClientLatestDepsTest extends AbstractCommonsHttpClientTest { - - @Override - int sendRequest(HttpMethod request, String method, URI uri, Map headers) { - try { - getClient(uri).executeMethod(null, request, null) - return request.getStatusCode() - } finally { - request.releaseConnection() - } - } -} diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientTest.groovy b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientTest.groovy deleted file mode 100644 index 7502f15c1603..000000000000 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientTest.groovy +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.apache.commons.httpclient.HttpMethod - -class CommonsHttpClientTest extends AbstractCommonsHttpClientTest { - - @Override - int sendRequest(HttpMethod request, String method, URI uri, Map headers) { - try { - getClient(uri).executeMethod(request) - return request.getStatusCode() - } finally { - request.releaseConnection() - } - } -} diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/AbstractCommonsHttpClientTest.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/AbstractCommonsHttpClientTest.java new file mode 100644 index 000000000000..3575b63f4d91 --- /dev/null +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/AbstractCommonsHttpClientTest.java @@ -0,0 +1,96 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import java.net.URI; +import java.util.Map; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.methods.OptionsMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.TraceMethod; +import org.junit.jupiter.api.extension.RegisterExtension; + +abstract class AbstractCommonsHttpClientTest extends AbstractHttpClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + private static final HttpConnectionManager connectionManager = + new MultiThreadedHttpConnectionManager(); + private static final HttpClient client = buildClient(false); + private static final HttpClient clientWithReadTimeout = buildClient(true); + + static HttpClient buildClient(boolean readTimeout) { + HttpClient client = new HttpClient(connectionManager); + client.setConnectionTimeout((int) CONNECTION_TIMEOUT.toMillis()); + if (readTimeout) { + client.setTimeout((int) READ_TIMEOUT.toMillis()); + } + return client; + } + + HttpClient getClient(URI uri) { + if (uri.toString().contains("/read-timeout")) { + return clientWithReadTimeout; + } + return client; + } + + @Override + public HttpMethod buildRequest(String method, URI uri, Map headers) { + HttpMethod request; + switch (method) { + case "GET": + request = new GetMethod(uri.toString()); + break; + case "PUT": + request = new PutMethod(uri.toString()); + break; + case "POST": + request = new PostMethod(uri.toString()); + break; + case "HEAD": + request = new HeadMethod(uri.toString()); + break; + case "DELETE": + request = new DeleteMethod(uri.toString()); + break; + case "OPTIONS": + request = new OptionsMethod(uri.toString()); + break; + case "TRACE": + request = new TraceMethod(uri.toString()); + break; + default: + throw new IllegalStateException("Unsupported method: " + method); + } + + for (Map.Entry entry : headers.entrySet()) { + request.setRequestHeader(entry.getKey(), entry.getValue()); + } + return request; + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + optionsBuilder + .disableTestCallback() + .disableTestReusedRequest() + .disableTestNonStandardHttpMethod() + .disableTestCircularRedirects(); + } +} diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientLatestDepsTest.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientLatestDepsTest.java new file mode 100644 index 000000000000..a482ddeff860 --- /dev/null +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientLatestDepsTest.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0; + +import java.net.URI; +import java.util.Map; +import org.apache.commons.httpclient.HttpMethod; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +// this test will be ignored if not executed with -PtestLatestDeps=true +// because the latest dependency commons-httpclient v3.1 allows a call to the executeMethod +// with some null parameters like HttpClient.executeMethod(null, request, null) +// but this construct is not allowed in commons-httpclient v2 that is used for regular otel testing +@EnabledIfSystemProperty(named = "testLatestDeps", matches = "true") +public class CommonsHttpClientLatestDepsTest extends AbstractCommonsHttpClientTest { + @Override + public int sendRequest(HttpMethod request, String method, URI uri, Map headers) + throws Exception { + try { + getClient(uri).executeMethod(null, request, null); + return request.getStatusCode(); + } finally { + request.releaseConnection(); + } + } +} diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientTest.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientTest.java new file mode 100644 index 000000000000..ddcbe7d3af1f --- /dev/null +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientTest.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0; + +import java.net.URI; +import java.util.Map; +import org.apache.commons.httpclient.HttpMethod; + +class CommonsHttpClientTest extends AbstractCommonsHttpClientTest { + + @Override + public int sendRequest(HttpMethod request, String method, URI uri, Map headers) + throws Exception { + try { + getClient(uri).executeMethod(request); + return request.getStatusCode(); + } finally { + request.releaseConnection(); + } + } +} diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java index 3526ca01436a..7508931128b1 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java @@ -98,7 +98,7 @@ public ApacheHttpClientTelemetryBuilder setEmitExperimentalHttpClientMetrics( @CanIgnoreReturnValue public ApacheHttpClientTelemetryBuilder setSpanNameExtractor( Function< - SpanNameExtractor, + SpanNameExtractor, ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.2/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v5_2/ApacheHttpClient5TelemetryBuilder.java b/instrumentation/apache-httpclient/apache-httpclient-5.2/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v5_2/ApacheHttpClient5TelemetryBuilder.java index e9a5eb273de0..b9b98eb5d4a6 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.2/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v5_2/ApacheHttpClient5TelemetryBuilder.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.2/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v5_2/ApacheHttpClient5TelemetryBuilder.java @@ -100,7 +100,7 @@ public ApacheHttpClient5TelemetryBuilder setEmitExperimentalHttpClientMetrics( @CanIgnoreReturnValue public ApacheHttpClient5TelemetryBuilder setSpanNameExtractor( Function< - SpanNameExtractor, + SpanNameExtractor, ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); diff --git a/instrumentation/armeria/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java b/instrumentation/armeria/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java index 8f4800ebca34..4845c528e7e1 100644 --- a/instrumentation/armeria/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java +++ b/instrumentation/armeria/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java @@ -8,9 +8,10 @@ import com.linecorp.armeria.client.HttpClient; import com.linecorp.armeria.server.HttpService; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaTelemetry; -import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaHttpClientAttributesGetter; +import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaTelemetryBuilder; +import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderUtil; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; import java.util.function.Function; @@ -22,28 +23,15 @@ public final class ArmeriaSingletons { public static final Function SERVER_DECORATOR; static { - ArmeriaTelemetry telemetry = - ArmeriaTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedClientRequestHeaders(AgentCommonConfig.get().getClientRequestHeaders()) - .setCapturedClientResponseHeaders(AgentCommonConfig.get().getClientResponseHeaders()) - .setCapturedServerRequestHeaders(AgentCommonConfig.get().getServerRequestHeaders()) - .setCapturedServerResponseHeaders(AgentCommonConfig.get().getServerResponseHeaders()) - .setKnownMethods(AgentCommonConfig.get().getKnownHttpRequestMethods()) - .addClientAttributeExtractor( - HttpClientPeerServiceAttributesExtractor.create( - ArmeriaHttpClientAttributesGetter.INSTANCE, - AgentCommonConfig.get().getPeerServiceResolver())) - .setEmitExperimentalHttpClientMetrics( - AgentCommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) - .setEmitExperimentalHttpServerMetrics( - AgentCommonConfig.get().shouldEmitExperimentalHttpServerTelemetry()) - .build(); + ArmeriaTelemetryBuilder builder = ArmeriaTelemetry.builder(GlobalOpenTelemetry.get()); + CommonConfig config = AgentCommonConfig.get(); + ArmeriaInstrumenterBuilderUtil.getClientBuilderExtractor().apply(builder).configure(config); + ArmeriaInstrumenterBuilderUtil.getServerBuilderExtractor().apply(builder).configure(config); + ArmeriaTelemetry telemetry = builder.build(); CLIENT_DECORATOR = telemetry.newClientDecorator(); Function libraryDecorator = - telemetry - .newServiceDecorator() - .compose(service -> new ResponseCustomizingDecorator(service)); + telemetry.newServiceDecorator().compose(ResponseCustomizingDecorator::new); SERVER_DECORATOR = service -> new ServerDecorator(service, libraryDecorator.apply(service)); } diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java index d39d8dcbb47a..5d6a17eb6586 100644 --- a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java @@ -11,98 +11,92 @@ import com.linecorp.armeria.common.logging.RequestLog; import com.linecorp.armeria.server.ServiceRequestContext; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaHttpClientAttributesGetter; -import java.util.ArrayList; +import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderFactory; +import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderUtil; import java.util.List; import java.util.Set; import java.util.function.Function; -import java.util.stream.Stream; -import javax.annotation.Nullable; public final class ArmeriaTelemetryBuilder { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.armeria-1.3"; - // copied from PeerIncubatingAttributes - private static final AttributeKey PEER_SERVICE = AttributeKey.stringKey("peer.service"); + private final DefaultHttpClientInstrumenterBuilder + clientBuilder; + private final DefaultHttpServerInstrumenterBuilder + serverBuilder; - private final OpenTelemetry openTelemetry; - @Nullable private String peerService; - private boolean emitExperimentalHttpClientMetrics = false; - private boolean emitExperimentalHttpServerMetrics = false; - - private final List> - additionalExtractors = new ArrayList<>(); - private final List> - additionalClientExtractors = new ArrayList<>(); - - private final HttpClientAttributesExtractorBuilder - httpClientAttributesExtractorBuilder = - HttpClientAttributesExtractor.builder(ArmeriaHttpClientAttributesGetter.INSTANCE); - private final HttpServerAttributesExtractorBuilder - httpServerAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(ArmeriaHttpServerAttributesGetter.INSTANCE); - - private final HttpSpanNameExtractorBuilder httpClientSpanNameExtractorBuilder = - HttpSpanNameExtractor.builder(ArmeriaHttpClientAttributesGetter.INSTANCE); - private final HttpSpanNameExtractorBuilder httpServerSpanNameExtractorBuilder = - HttpSpanNameExtractor.builder(ArmeriaHttpServerAttributesGetter.INSTANCE); - private Function< - SpanNameExtractor, ? extends SpanNameExtractor> - clientSpanNameExtractorTransformer = Function.identity(); - private Function< - SpanNameExtractor, ? extends SpanNameExtractor> - serverSpanNameExtractorTransformer = Function.identity(); - - private final HttpServerRouteBuilder httpServerRouteBuilder = - HttpServerRoute.builder(ArmeriaHttpServerAttributesGetter.INSTANCE); - - private Function< - SpanStatusExtractor, - ? extends SpanStatusExtractor> - statusExtractorTransformer = Function.identity(); + static { + ArmeriaInstrumenterBuilderUtil.setClientBuilderExtractor( + ArmeriaTelemetryBuilder::getClientBuilder); + ArmeriaInstrumenterBuilderUtil.setServerBuilderExtractor( + ArmeriaTelemetryBuilder::getServerBuilder); + } ArmeriaTelemetryBuilder(OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; + clientBuilder = ArmeriaInstrumenterBuilderFactory.getClientBuilder(openTelemetry); + serverBuilder = ArmeriaInstrumenterBuilderFactory.getServerBuilder(openTelemetry); } + /** + * Sets the status extractor for both client and server spans. + * + * @deprecated Use {@link #setClientStatusExtractor(Function)} or {@link + * #setServerStatusExtractor(Function)} instead. + */ + @Deprecated + @SuppressWarnings({"unchecked", "rawtypes"}) @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setStatusExtractor( Function< SpanStatusExtractor, ? extends SpanStatusExtractor> statusExtractor) { - this.statusExtractorTransformer = statusExtractor; + clientBuilder.setStatusExtractor((Function) statusExtractor); + serverBuilder.setStatusExtractor((Function) statusExtractor); + return this; + } + + /** Sets the status extractor for client spans. */ + @CanIgnoreReturnValue + public ArmeriaTelemetryBuilder setClientStatusExtractor( + Function< + SpanStatusExtractor, + ? extends SpanStatusExtractor> + statusExtractor) { + clientBuilder.setStatusExtractor(statusExtractor); + return this; + } + + /** Sets the status extractor for server spans. */ + @CanIgnoreReturnValue + public ArmeriaTelemetryBuilder setServerStatusExtractor( + Function< + SpanStatusExtractor, + ? extends SpanStatusExtractor> + statusExtractor) { + serverBuilder.setStatusExtractor(statusExtractor); return this; } /** * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. The {@link AttributesExtractor} will be executed after all default extractors. + * + * @deprecated Use {@link #addClientAttributeExtractor(AttributesExtractor)} or {@link + * #addServerAttributeExtractor(AttributesExtractor)} instead. */ + @Deprecated @CanIgnoreReturnValue public ArmeriaTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { - additionalExtractors.add(attributesExtractor); + clientBuilder.addAttributeExtractor(attributesExtractor); + serverBuilder.addAttributesExtractor(attributesExtractor); return this; } @@ -114,14 +108,26 @@ public ArmeriaTelemetryBuilder addAttributeExtractor( @CanIgnoreReturnValue public ArmeriaTelemetryBuilder addClientAttributeExtractor( AttributesExtractor attributesExtractor) { - additionalClientExtractors.add(attributesExtractor); + clientBuilder.addAttributeExtractor(attributesExtractor); + return this; + } + + /** + * Adds an extra server-only {@link AttributesExtractor} to invoke to set attributes to + * instrumented items. The {@link AttributesExtractor} will be executed after all default + * extractors. + */ + @CanIgnoreReturnValue + public ArmeriaTelemetryBuilder addServerAttributeExtractor( + AttributesExtractor attributesExtractor) { + serverBuilder.addAttributesExtractor(attributesExtractor); return this; } /** Sets the {@code peer.service} attribute for http client spans. */ @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setPeerService(String peerService) { - this.peerService = peerService; + clientBuilder.setPeerService(peerService); return this; } @@ -132,7 +138,7 @@ public ArmeriaTelemetryBuilder setPeerService(String peerService) { */ @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedClientRequestHeaders(List requestHeaders) { - httpClientAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); + clientBuilder.setCapturedRequestHeaders(requestHeaders); return this; } @@ -143,7 +149,7 @@ public ArmeriaTelemetryBuilder setCapturedClientRequestHeaders(List requ */ @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedClientResponseHeaders(List responseHeaders) { - httpClientAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); + clientBuilder.setCapturedResponseHeaders(responseHeaders); return this; } @@ -154,7 +160,7 @@ public ArmeriaTelemetryBuilder setCapturedClientResponseHeaders(List res */ @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedServerRequestHeaders(List requestHeaders) { - httpServerAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); + serverBuilder.setCapturedRequestHeaders(requestHeaders); return this; } @@ -165,7 +171,7 @@ public ArmeriaTelemetryBuilder setCapturedServerRequestHeaders(List requ */ @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedServerResponseHeaders(List responseHeaders) { - httpServerAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); + serverBuilder.setCapturedResponseHeaders(responseHeaders); return this; } @@ -185,11 +191,8 @@ public ArmeriaTelemetryBuilder setCapturedServerResponseHeaders(List res */ @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setKnownMethods(Set knownMethods) { - httpClientAttributesExtractorBuilder.setKnownMethods(knownMethods); - httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods); - httpClientSpanNameExtractorBuilder.setKnownMethods(knownMethods); - httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods); - httpServerRouteBuilder.setKnownMethods(knownMethods); + clientBuilder.setKnownMethods(knownMethods); + serverBuilder.setKnownMethods(knownMethods); return this; } @@ -202,7 +205,7 @@ public ArmeriaTelemetryBuilder setKnownMethods(Set knownMethods) { @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setEmitExperimentalHttpClientMetrics( boolean emitExperimentalHttpClientMetrics) { - this.emitExperimentalHttpClientMetrics = emitExperimentalHttpClientMetrics; + clientBuilder.setEmitExperimentalHttpClientMetrics(emitExperimentalHttpClientMetrics); return this; } @@ -215,7 +218,7 @@ public ArmeriaTelemetryBuilder setEmitExperimentalHttpClientMetrics( @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setEmitExperimentalHttpServerMetrics( boolean emitExperimentalHttpServerMetrics) { - this.emitExperimentalHttpServerMetrics = emitExperimentalHttpServerMetrics; + serverBuilder.setEmitExperimentalHttpServerMetrics(emitExperimentalHttpServerMetrics); return this; } @@ -223,10 +226,10 @@ public ArmeriaTelemetryBuilder setEmitExperimentalHttpServerMetrics( @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setClientSpanNameExtractor( Function< - SpanNameExtractor, - ? extends SpanNameExtractor> + SpanNameExtractor, + ? extends SpanNameExtractor> clientSpanNameExtractor) { - this.clientSpanNameExtractorTransformer = clientSpanNameExtractor; + clientBuilder.setSpanNameExtractor(clientSpanNameExtractor); return this; } @@ -234,66 +237,24 @@ public ArmeriaTelemetryBuilder setClientSpanNameExtractor( @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setServerSpanNameExtractor( Function< - SpanNameExtractor, - ? extends SpanNameExtractor> + SpanNameExtractor, + ? extends SpanNameExtractor> serverSpanNameExtractor) { - this.serverSpanNameExtractorTransformer = serverSpanNameExtractor; + serverBuilder.setSpanNameExtractor(serverSpanNameExtractor); return this; } public ArmeriaTelemetry build() { - ArmeriaHttpClientAttributesGetter clientAttributesGetter = - ArmeriaHttpClientAttributesGetter.INSTANCE; - ArmeriaHttpServerAttributesGetter serverAttributesGetter = - ArmeriaHttpServerAttributesGetter.INSTANCE; - - SpanNameExtractor clientSpanNameExtractor = - clientSpanNameExtractorTransformer.apply(httpClientSpanNameExtractorBuilder.build()); - SpanNameExtractor serverSpanNameExtractor = - serverSpanNameExtractorTransformer.apply(httpServerSpanNameExtractorBuilder.build()); - - InstrumenterBuilder clientInstrumenterBuilder = - Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, clientSpanNameExtractor); - InstrumenterBuilder serverInstrumenterBuilder = - Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, serverSpanNameExtractor); - - Stream.of(clientInstrumenterBuilder, serverInstrumenterBuilder) - .forEach(instrumenter -> instrumenter.addAttributesExtractors(additionalExtractors)); - - clientInstrumenterBuilder - .setSpanStatusExtractor( - statusExtractorTransformer.apply( - HttpSpanStatusExtractor.create(clientAttributesGetter))) - .addAttributesExtractor(httpClientAttributesExtractorBuilder.build()) - .addAttributesExtractors(additionalClientExtractors) - .addOperationMetrics(HttpClientMetrics.get()); - serverInstrumenterBuilder - .setSpanStatusExtractor( - statusExtractorTransformer.apply( - HttpSpanStatusExtractor.create(serverAttributesGetter))) - .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) - .addOperationMetrics(HttpServerMetrics.get()) - .addContextCustomizer(httpServerRouteBuilder.build()); + return new ArmeriaTelemetry(clientBuilder.build(), serverBuilder.build()); + } - if (peerService != null) { - clientInstrumenterBuilder.addAttributesExtractor( - AttributesExtractor.constant(PEER_SERVICE, peerService)); - } - if (emitExperimentalHttpClientMetrics) { - clientInstrumenterBuilder - .addAttributesExtractor( - HttpExperimentalAttributesExtractor.create(clientAttributesGetter)) - .addOperationMetrics(HttpClientExperimentalMetrics.get()); - } - if (emitExperimentalHttpServerMetrics) { - serverInstrumenterBuilder - .addAttributesExtractor( - HttpExperimentalAttributesExtractor.create(serverAttributesGetter)) - .addOperationMetrics(HttpServerExperimentalMetrics.get()); - } + private DefaultHttpClientInstrumenterBuilder + getClientBuilder() { + return clientBuilder; + } - return new ArmeriaTelemetry( - clientInstrumenterBuilder.buildClientInstrumenter(ClientRequestContextSetter.INSTANCE), - serverInstrumenterBuilder.buildServerInstrumenter(RequestContextGetter.INSTANCE)); + private DefaultHttpServerInstrumenterBuilder + getServerBuilder() { + return serverBuilder; } } diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpClientAttributesGetter.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpClientAttributesGetter.java index ec6d00b8d6e2..29dfa4aca00b 100644 --- a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpClientAttributesGetter.java +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpClientAttributesGetter.java @@ -5,9 +5,9 @@ package io.opentelemetry.instrumentation.armeria.v1_3.internal; +import com.linecorp.armeria.client.ClientRequestContext; import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.common.HttpStatus; -import com.linecorp.armeria.common.RequestContext; import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.common.logging.RequestLog; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter; @@ -16,12 +16,8 @@ import java.util.List; import javax.annotation.Nullable; -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public enum ArmeriaHttpClientAttributesGetter - implements HttpClientAttributesGetter { +enum ArmeriaHttpClientAttributesGetter + implements HttpClientAttributesGetter { INSTANCE; private static final ClassValue authorityMethodCache = @@ -38,12 +34,12 @@ protected Method computeValue(Class type) { }; @Override - public String getHttpRequestMethod(RequestContext ctx) { + public String getHttpRequestMethod(ClientRequestContext ctx) { return ctx.method().name(); } @Override - public String getUrlFull(RequestContext ctx) { + public String getUrlFull(ClientRequestContext ctx) { HttpRequest request = request(ctx); StringBuilder uri = new StringBuilder(); String scheme = request.scheme(); @@ -65,14 +61,14 @@ public String getUrlFull(RequestContext ctx) { } @Override - public List getHttpRequestHeader(RequestContext ctx, String name) { + public List getHttpRequestHeader(ClientRequestContext ctx, String name) { return request(ctx).headers().getAll(name); } @Override @Nullable public Integer getHttpResponseStatusCode( - RequestContext ctx, RequestLog requestLog, @Nullable Throwable error) { + ClientRequestContext ctx, RequestLog requestLog, @Nullable Throwable error) { HttpStatus status = requestLog.responseHeaders().status(); if (!status.equals(HttpStatus.UNKNOWN)) { return status.code(); @@ -82,17 +78,18 @@ public Integer getHttpResponseStatusCode( @Override public List getHttpResponseHeader( - RequestContext ctx, RequestLog requestLog, String name) { + ClientRequestContext ctx, RequestLog requestLog, String name) { return requestLog.responseHeaders().getAll(name); } @Override - public String getNetworkProtocolName(RequestContext ctx, @Nullable RequestLog requestLog) { + public String getNetworkProtocolName(ClientRequestContext ctx, @Nullable RequestLog requestLog) { return "http"; } @Override - public String getNetworkProtocolVersion(RequestContext ctx, @Nullable RequestLog requestLog) { + public String getNetworkProtocolVersion( + ClientRequestContext ctx, @Nullable RequestLog requestLog) { SessionProtocol protocol = requestLog != null ? requestLog.sessionProtocol() : ctx.sessionProtocol(); return protocol.isMultiplex() ? "2" : "1.1"; @@ -100,7 +97,7 @@ public String getNetworkProtocolVersion(RequestContext ctx, @Nullable RequestLog @Nullable @Override - public String getServerAddress(RequestContext ctx) { + public String getServerAddress(ClientRequestContext ctx) { String authority = authority(ctx); if (authority == null) { return null; @@ -111,7 +108,7 @@ public String getServerAddress(RequestContext ctx) { @Nullable @Override - public Integer getServerPort(RequestContext ctx) { + public Integer getServerPort(ClientRequestContext ctx) { String authority = authority(ctx); if (authority == null) { return null; @@ -130,12 +127,12 @@ public Integer getServerPort(RequestContext ctx) { @Override @Nullable public InetSocketAddress getNetworkPeerInetSocketAddress( - RequestContext ctx, @Nullable RequestLog requestLog) { + ClientRequestContext ctx, @Nullable RequestLog requestLog) { return RequestContextAccess.remoteAddress(ctx); } @Nullable - private static String authority(RequestContext ctx) { + private static String authority(ClientRequestContext ctx) { // newer armeria versions expose authority through DefaultClientRequestContext#authority // we are using this method as it provides default values based on endpoint // in older versions armeria wraps the request, and we can get the same default values through @@ -153,7 +150,7 @@ private static String authority(RequestContext ctx) { return request.authority(); } - private static HttpRequest request(RequestContext ctx) { + private static HttpRequest request(ClientRequestContext ctx) { HttpRequest request = ctx.request(); if (request == null) { throw new IllegalStateException( diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpServerAttributesGetter.java similarity index 63% rename from instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java rename to instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpServerAttributesGetter.java index 1f5891d9c43f..5cef09ca7fd9 100644 --- a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaHttpServerAttributesGetter.java @@ -3,37 +3,35 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.armeria.v1_3; +package io.opentelemetry.instrumentation.armeria.v1_3.internal; import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.common.HttpStatus; -import com.linecorp.armeria.common.RequestContext; import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.common.logging.RequestLog; import com.linecorp.armeria.server.ServiceRequestContext; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesGetter; -import io.opentelemetry.instrumentation.armeria.v1_3.internal.RequestContextAccess; import java.net.InetSocketAddress; import java.util.List; import javax.annotation.Nullable; enum ArmeriaHttpServerAttributesGetter - implements HttpServerAttributesGetter { + implements HttpServerAttributesGetter { INSTANCE; @Override - public String getHttpRequestMethod(RequestContext ctx) { + public String getHttpRequestMethod(ServiceRequestContext ctx) { return ctx.method().name(); } @Override @Nullable - public String getUrlScheme(RequestContext ctx) { + public String getUrlScheme(ServiceRequestContext ctx) { return request(ctx).scheme(); } @Override - public String getUrlPath(RequestContext ctx) { + public String getUrlPath(ServiceRequestContext ctx) { String fullPath = request(ctx).path(); int separatorPos = fullPath.indexOf('?'); return separatorPos == -1 ? fullPath : fullPath.substring(0, separatorPos); @@ -41,21 +39,21 @@ public String getUrlPath(RequestContext ctx) { @Nullable @Override - public String getUrlQuery(RequestContext ctx) { + public String getUrlQuery(ServiceRequestContext ctx) { String fullPath = request(ctx).path(); int separatorPos = fullPath.indexOf('?'); return separatorPos == -1 ? null : fullPath.substring(separatorPos + 1); } @Override - public List getHttpRequestHeader(RequestContext ctx, String name) { + public List getHttpRequestHeader(ServiceRequestContext ctx, String name) { return request(ctx).headers().getAll(name); } @Override @Nullable public Integer getHttpResponseStatusCode( - RequestContext ctx, RequestLog requestLog, @Nullable Throwable error) { + ServiceRequestContext ctx, RequestLog requestLog, @Nullable Throwable error) { HttpStatus status = requestLog.responseHeaders().status(); if (!status.equals(HttpStatus.UNKNOWN)) { return status.code(); @@ -65,26 +63,24 @@ public Integer getHttpResponseStatusCode( @Override public List getHttpResponseHeader( - RequestContext ctx, RequestLog requestLog, String name) { + ServiceRequestContext ctx, RequestLog requestLog, String name) { return requestLog.responseHeaders().getAll(name); } @Override @Nullable - public String getHttpRoute(RequestContext ctx) { - if (ctx instanceof ServiceRequestContext) { - return ((ServiceRequestContext) ctx).config().route().patternString(); - } - return null; + public String getHttpRoute(ServiceRequestContext ctx) { + return ctx.config().route().patternString(); } @Override - public String getNetworkProtocolName(RequestContext ctx, @Nullable RequestLog requestLog) { + public String getNetworkProtocolName(ServiceRequestContext ctx, @Nullable RequestLog requestLog) { return "http"; } @Override - public String getNetworkProtocolVersion(RequestContext ctx, @Nullable RequestLog requestLog) { + public String getNetworkProtocolVersion( + ServiceRequestContext ctx, @Nullable RequestLog requestLog) { SessionProtocol protocol = ctx.sessionProtocol(); return protocol.isMultiplex() ? "2" : "1.1"; } @@ -92,18 +88,18 @@ public String getNetworkProtocolVersion(RequestContext ctx, @Nullable RequestLog @Override @Nullable public InetSocketAddress getNetworkPeerInetSocketAddress( - RequestContext ctx, @Nullable RequestLog requestLog) { + ServiceRequestContext ctx, @Nullable RequestLog requestLog) { return RequestContextAccess.remoteAddress(ctx); } @Nullable @Override public InetSocketAddress getNetworkLocalInetSocketAddress( - RequestContext ctx, @Nullable RequestLog log) { + ServiceRequestContext ctx, @Nullable RequestLog log) { return RequestContextAccess.localAddress(ctx); } - private static HttpRequest request(RequestContext ctx) { + private static HttpRequest request(ServiceRequestContext ctx) { HttpRequest request = ctx.request(); if (request == null) { throw new IllegalStateException( diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaInstrumenterBuilderFactory.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaInstrumenterBuilderFactory.java new file mode 100644 index 000000000000..07a28da6da85 --- /dev/null +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaInstrumenterBuilderFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.armeria.v1_3.internal; + +import com.linecorp.armeria.client.ClientRequestContext; +import com.linecorp.armeria.common.logging.RequestLog; +import com.linecorp.armeria.server.ServiceRequestContext; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class ArmeriaInstrumenterBuilderFactory { + private ArmeriaInstrumenterBuilderFactory() {} + + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.armeria-1.3"; + + public static DefaultHttpServerInstrumenterBuilder + getServerBuilder(OpenTelemetry openTelemetry) { + return new DefaultHttpServerInstrumenterBuilder<>( + INSTRUMENTATION_NAME, openTelemetry, ArmeriaHttpServerAttributesGetter.INSTANCE) + .setHeaderGetter(RequestContextGetter.INSTANCE); + } + + public static DefaultHttpClientInstrumenterBuilder + getClientBuilder(OpenTelemetry openTelemetry) { + return new DefaultHttpClientInstrumenterBuilder<>( + INSTRUMENTATION_NAME, openTelemetry, ArmeriaHttpClientAttributesGetter.INSTANCE) + .setHeaderSetter(ClientRequestContextSetter.INSTANCE); + } +} diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaInstrumenterBuilderUtil.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaInstrumenterBuilderUtil.java new file mode 100644 index 000000000000..a9a7d8b43b09 --- /dev/null +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaInstrumenterBuilderUtil.java @@ -0,0 +1,67 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.armeria.v1_3.internal; + +import com.linecorp.armeria.client.ClientRequestContext; +import com.linecorp.armeria.common.logging.RequestLog; +import com.linecorp.armeria.server.ServiceRequestContext; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; +import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaTelemetryBuilder; +import java.util.function.Function; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class ArmeriaInstrumenterBuilderUtil { + private ArmeriaInstrumenterBuilderUtil() {} + + @Nullable + private static Function< + ArmeriaTelemetryBuilder, + DefaultHttpClientInstrumenterBuilder> + clientBuilderExtractor; + + @Nullable + private static Function< + ArmeriaTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + serverBuilderExtractor; + + @Nullable + public static Function< + ArmeriaTelemetryBuilder, + DefaultHttpClientInstrumenterBuilder> + getClientBuilderExtractor() { + return clientBuilderExtractor; + } + + public static void setClientBuilderExtractor( + Function< + ArmeriaTelemetryBuilder, + DefaultHttpClientInstrumenterBuilder> + clientBuilderExtractor) { + ArmeriaInstrumenterBuilderUtil.clientBuilderExtractor = clientBuilderExtractor; + } + + @Nullable + public static Function< + ArmeriaTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + getServerBuilderExtractor() { + return serverBuilderExtractor; + } + + public static void setServerBuilderExtractor( + Function< + ArmeriaTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + serverBuilderExtractor) { + ArmeriaInstrumenterBuilderUtil.serverBuilderExtractor = serverBuilderExtractor; + } +} diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ClientRequestContextSetter.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ClientRequestContextSetter.java similarity index 89% rename from instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ClientRequestContextSetter.java rename to instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ClientRequestContextSetter.java index 91ce53e54805..1a225fd7108b 100644 --- a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ClientRequestContextSetter.java +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ClientRequestContextSetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.armeria.v1_3; +package io.opentelemetry.instrumentation.armeria.v1_3.internal; import com.linecorp.armeria.client.ClientRequestContext; import io.opentelemetry.context.propagation.TextMapSetter; diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextAccess.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextAccess.java index acaf7fd9cfa0..1a7830758fd6 100644 --- a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextAccess.java +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextAccess.java @@ -13,11 +13,7 @@ import java.net.SocketAddress; import javax.annotation.Nullable; -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public final class RequestContextAccess { +final class RequestContextAccess { @Nullable private static final MethodHandle remoteAddress = findAccessorOrNull("remoteAddress"); @Nullable private static final MethodHandle localAddress = findAccessorOrNull("localAddress"); diff --git a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/RequestContextGetter.java b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextGetter.java similarity index 93% rename from instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/RequestContextGetter.java rename to instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextGetter.java index 5163333591a0..5cae10de6578 100644 --- a/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/RequestContextGetter.java +++ b/instrumentation/armeria/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/RequestContextGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.armeria.v1_3; +package io.opentelemetry.instrumentation.armeria.v1_3.internal; import com.linecorp.armeria.server.ServiceRequestContext; import io.netty.util.AsciiString; diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/CassandraTest.java b/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraTest.java similarity index 82% rename from instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/CassandraTest.java rename to instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraTest.java index 4db6a86efbdb..1df7618a85c9 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/CassandraTest.java +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraTest.java @@ -3,12 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.javaagent.instrumentation.cassandra.v4_0; + import io.opentelemetry.cassandra.v4.common.AbstractCassandraTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import org.junit.jupiter.api.extension.RegisterExtension; -public class CassandraTest extends AbstractCassandraTest { +class CassandraTest extends AbstractCassandraTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/ChannelTransportInstrumentation.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/ChannelTransportInstrumentation.java index dd2f1b5ef0a4..f7490431ff92 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/ChannelTransportInstrumentation.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/ChannelTransportInstrumentation.java @@ -34,16 +34,16 @@ public void transform(TypeTransformer transformer) { public static class WriteAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter(@Advice.Local("otelScope") Scope scope) { + public static Scope methodEnter() { Option ref = Helpers.CONTEXT_LOCAL.apply(); if (ref.isDefined()) { - scope = ref.get().makeCurrent(); + return ref.get().makeCurrent(); } + return null; } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - public static void methodExit( - @Advice.Local("otelScope") Scope scope, @Advice.Thrown Throwable thrown) { + public static void methodExit(@Advice.Enter Scope scope, @Advice.Thrown Throwable thrown) { if (scope != null) { scope.close(); } diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/FinagleHttpInstrumentationModule.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/FinagleHttpInstrumentationModule.java index ab8f9c9c9919..3938e762bfa4 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/FinagleHttpInstrumentationModule.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/FinagleHttpInstrumentationModule.java @@ -8,11 +8,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.Arrays; import java.util.List; @AutoService(InstrumentationModule.class) -public class FinagleHttpInstrumentationModule extends InstrumentationModule { +public class FinagleHttpInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public FinagleHttpInstrumentationModule() { super("finagle-http", "finagle-http-23.11"); @@ -27,9 +29,17 @@ public List typeInstrumentations() { } @Override - public boolean isIndyModule() { - // injects helpers to access package-private members - return false; + public String getModuleGroup() { + // relies on netty and needs access to common netty instrumentation classes + return "netty"; + } + + @Override + public List injectedClassNames() { + // these are injected so that they can access package-private members + return Arrays.asList( + "com.twitter.finagle.ChannelTransportHelpers", + "io.netty.channel.OpenTelemetryChannelInitializerDelegate"); } @Override diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/H2StreamChannelInitInstrumentation.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/H2StreamChannelInitInstrumentation.java index 33862053cb9e..cfa7176077c7 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/H2StreamChannelInitInstrumentation.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/H2StreamChannelInitInstrumentation.java @@ -42,9 +42,10 @@ public void transform(TypeTransformer transformer) { public static class InitServerAdvice { @Advice.OnMethodExit - public static void handleExit( - @Advice.Return(readOnly = false) ChannelInitializer initializer) { - initializer = Helpers.wrapServer(initializer); + @Advice.AssignReturned.ToReturned + public static ChannelInitializer handleExit( + @Advice.Return ChannelInitializer initializer) { + return Helpers.wrapServer(initializer); } } @@ -52,9 +53,10 @@ public static void handleExit( public static class InitClientAdvice { @Advice.OnMethodExit - public static void handleExit( - @Advice.Return(readOnly = false) ChannelInitializer initializer) { - initializer = Helpers.wrapClient(initializer); + @Advice.AssignReturned.ToReturned + public static ChannelInitializer handleExit( + @Advice.Return ChannelInitializer initializer) { + return Helpers.wrapClient(initializer); } } } diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/Helpers.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/Helpers.java index 4eed55e13751..6f00d4cd69d9 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/Helpers.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/Helpers.java @@ -18,12 +18,11 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; -import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler; import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerTracingHandler; import io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyHttpServerResponseBeforeCommitHandler; import io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyServerSingletons; -import java.util.Deque; public final class Helpers { @@ -42,9 +41,8 @@ protected void initChannel(C channel) throws Exception { // the parent channel is the original http/1.1 channel and has the contexts stored in it; // we assign to this new channel as the old one will not be evaluated in the upgraded h2c // chain - Deque serverContexts = - channel.parent().attr(AttributeKeys.SERVER_CONTEXT).get(); - channel.attr(AttributeKeys.SERVER_CONTEXT).set(serverContexts); + ServerContexts serverContexts = ServerContexts.get(channel.parent()); + channel.attr(AttributeKeys.SERVER_CONTEXTS).set(serverContexts); // todo add way to propagate the protocol version override up to the netty instrumentation; // why: the netty instrumentation extracts the http protocol version from the HttpRequest diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/LocalSchedulerActivationInstrumentation.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/LocalSchedulerActivationInstrumentation.java index f996916f816b..1cbec8c9c0fd 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/LocalSchedulerActivationInstrumentation.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/LocalSchedulerActivationInstrumentation.java @@ -14,6 +14,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -35,13 +36,14 @@ public void transform(TypeTransformer transformer) { public static class WrapRunnableAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrap(@Advice.Argument(value = 0, readOnly = false) Runnable task) { + @Advice.AssignReturned.ToArguments(@ToArgument(0)) + public static Runnable wrap(@Advice.Argument(0) Runnable task) { if (task == null) { - return; + return null; } Context context = Java8BytecodeBridge.currentContext(); - task = ContextPropagatingRunnable.propagateContext(task, context); + return ContextPropagatingRunnable.propagateContext(task, context); } } } diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/PromiseMonitoredInstrumentation.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/PromiseMonitoredInstrumentation.java index 2cd637f4d385..bef50738b3ec 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/PromiseMonitoredInstrumentation.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/PromiseMonitoredInstrumentation.java @@ -12,6 +12,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import scala.Function1; @@ -34,13 +35,13 @@ public void transform(TypeTransformer transformer) { public static class WrapFunctionAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrap( - @Advice.Argument(value = 1, readOnly = false) Function1 function1) { + @Advice.AssignReturned.ToArguments(@ToArgument(1)) + public static Function1 wrap(@Advice.Argument(1) Function1 function1) { if (function1 == null) { - return; + return null; } - function1 = Function1Wrapper.wrap(function1); + return Function1Wrapper.wrap(function1); } } } diff --git a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/TwitterUtilCoreInstrumentationModule.java b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/TwitterUtilCoreInstrumentationModule.java index 022dea36455d..1f72e97f352c 100644 --- a/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/TwitterUtilCoreInstrumentationModule.java +++ b/instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/TwitterUtilCoreInstrumentationModule.java @@ -10,15 +10,22 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; @AutoService(InstrumentationModule.class) -public class TwitterUtilCoreInstrumentationModule extends InstrumentationModule { +public class TwitterUtilCoreInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public TwitterUtilCoreInstrumentationModule() { super("twitter-util-core"); } + @Override + public String getModuleGroup() { + return "netty"; + } + @Override public List typeInstrumentations() { return asList( diff --git a/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java b/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java index 85d91c7501db..11abd923af40 100644 --- a/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java +++ b/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java @@ -35,7 +35,6 @@ import java.util.Locale; import java.util.Map; import java.util.function.Consumer; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.ServerProperties; @@ -183,8 +182,7 @@ public List> errorPageSpanAssertions( if (endpoint == NOT_FOUND) { spanAssertions.add( span -> - span.satisfies( - spanData -> Assertions.assertThat(spanData.getName()).endsWith(".sendError")) + span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError")) .hasKind(SpanKind.INTERNAL) .hasAttributesSatisfying(Attributes::isEmpty)); } diff --git a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java index da2180df7b4f..5aa6f1aa9a41 100644 --- a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java +++ b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.grpc.v1_6; +import static org.assertj.core.api.Assertions.assertThat; + import example.GreeterGrpc; import example.Helloworld; import io.grpc.BindableService; @@ -23,7 +25,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import java.util.Collections; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -124,7 +125,7 @@ public void sayHello( "parent", () -> client.sayHello(Helloworld.Request.newBuilder().setName("test").build())); - OpenTelemetryAssertions.assertThat(response.getMessage()).isEqualTo("Hello test"); + assertThat(response.getMessage()).isEqualTo("Hello test"); testing() .waitAndAssertTraces( diff --git a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java index 0e41014eb4c3..bc812d26babd 100644 --- a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java +++ b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java @@ -46,7 +46,6 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; @@ -1643,7 +1642,7 @@ public void sayHello( "parent", () -> client.sayHello(Helloworld.Request.newBuilder().setName("test").build())); - OpenTelemetryAssertions.assertThat(response.getMessage()).isEqualTo("Hello test"); + assertThat(response.getMessage()).isEqualTo("Hello test"); testing() .waitAndAssertTraces( diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java index e995738cd723..962d82fea4b3 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java @@ -13,13 +13,12 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -50,17 +49,11 @@ public void transform(TypeTransformer transformer) { public static class CriteriaMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( - @Advice.This Criteria criteria, - @Advice.Origin("#m") String name, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + public static HibernateOperationScope startMethod( + @Advice.This Criteria criteria, @Advice.Origin("#m") String name) { + + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } String entityName = null; @@ -73,31 +66,17 @@ public static void startMethod( SessionInfo sessionInfo = criteriaVirtualField.get(criteria); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = new HibernateOperation("Criteria." + name, entityName, sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } + HibernateOperation hibernateOperation = + new HibernateOperation("Criteria." + name, entityName, sessionInfo); - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/HibernateInstrumentationModule.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/HibernateInstrumentationModule.java index d9f6d5a919ac..8a9dd3a82040 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/HibernateInstrumentationModule.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/HibernateInstrumentationModule.java @@ -11,11 +11,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class HibernateInstrumentationModule extends InstrumentationModule { +public class HibernateInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public HibernateInstrumentationModule() { super("hibernate", "hibernate-3.3"); @@ -30,6 +32,11 @@ public ElementMatcher.Junction classLoaderMatcher() { "org.hibernate.transaction.JBossTransactionManagerLookup"); } + @Override + public String getModuleGroup() { + return "hibernate"; + } + @Override public List typeInstrumentations() { return asList( diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java index 5a9742dcbcd0..e978128b4b22 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java @@ -14,13 +14,12 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -50,16 +49,10 @@ public void transform(TypeTransformer transformer) { public static class QueryMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( - @Advice.This Query query, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static HibernateOperationScope startMethod(@Advice.This Query query) { - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField queryVirtualField = @@ -67,32 +60,17 @@ public static void startMethod( SessionInfo sessionInfo = queryVirtualField.get(query); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation(getOperationNameForQuery(query.getQueryString()), sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java index 8cdefda3d53d..b01cd3b5bc7a 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java @@ -18,13 +18,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -92,52 +91,32 @@ public void transform(TypeTransformer transformer) { public static class SessionMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( + public static HibernateOperationScope startMethod( @Advice.This Object session, @Advice.Origin("#m") String name, @Advice.Origin("#d") String descriptor, @Advice.Argument(0) Object arg0, - @Advice.Argument(value = 1, optional = true) Object arg1, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + @Advice.Argument(value = 1, optional = true) Object arg1) { + + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } Context parentContext = Java8BytecodeBridge.currentContext(); SessionInfo sessionInfo = SessionUtil.getSessionInfo(session); String entityName = getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session)); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope enterScope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(enterScope, throwable); } } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java index c9f8631f54b8..4004151e1cd2 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java @@ -13,13 +13,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -49,16 +48,10 @@ public void transform(TypeTransformer transformer) { public static class TransactionCommitAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startCommit( - @Advice.This Transaction transaction, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static HibernateOperationScope startCommit(@Advice.This Transaction transaction) { - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField transactionVirtualField = @@ -66,31 +59,17 @@ public static void startCommit( SessionInfo sessionInfo = transactionVirtualField.get(transaction); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = new HibernateOperation("Transaction.commit", sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } + HibernateOperation hibernateOperation = + new HibernateOperation("Transaction.commit", sessionInfo); - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endCommit( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java index aec640bc6b07..495d6f9c595b 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java @@ -13,13 +13,12 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -50,17 +49,11 @@ public void transform(TypeTransformer transformer) { public static class CriteriaMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( - @Advice.This Criteria criteria, - @Advice.Origin("#m") String name, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + public static HibernateOperationScope startMethod( + @Advice.This Criteria criteria, @Advice.Origin("#m") String name) { + + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } String entityName = null; @@ -73,31 +66,17 @@ public static void startMethod( SessionInfo sessionInfo = criteriaVirtualField.get(criteria); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = new HibernateOperation("Criteria." + name, entityName, sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } + HibernateOperation hibernateOperation = + new HibernateOperation("Criteria." + name, entityName, sessionInfo); - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/HibernateInstrumentationModule.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/HibernateInstrumentationModule.java index 9685997529c2..e8a919d13c37 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/HibernateInstrumentationModule.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/HibernateInstrumentationModule.java @@ -11,11 +11,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class HibernateInstrumentationModule extends InstrumentationModule { +public class HibernateInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public HibernateInstrumentationModule() { super("hibernate", "hibernate-4.0"); @@ -31,10 +33,8 @@ public ElementMatcher.Junction classLoaderMatcher() { } @Override - public boolean isIndyModule() { - // shares classes with hibernate-procedure-call-4.3, these classes should be in the same class - // loader - return false; + public String getModuleGroup() { + return "hibernate"; } @Override diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java index af0618e6c773..635634ea73fc 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java @@ -14,13 +14,12 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -50,16 +49,10 @@ public void transform(TypeTransformer transformer) { public static class QueryMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( - @Advice.This Query query, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static HibernateOperationScope startMethod(@Advice.This Query query) { - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField queryVirtualField = @@ -67,32 +60,17 @@ public static void startMethod( SessionInfo sessionInfo = queryVirtualField.get(query); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation(getOperationNameForQuery(query.getQueryString()), sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java index e5e9fbc5116e..9457fbfbee51 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java @@ -18,13 +18,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -96,20 +95,15 @@ public void transform(TypeTransformer transformer) { public static class SessionMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( + public static HibernateOperationScope startMethod( @Advice.This SharedSessionContract session, @Advice.Origin("#m") String name, @Advice.Origin("#d") String descriptor, @Advice.Argument(0) Object arg0, - @Advice.Argument(value = 1, optional = true) Object arg1, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + @Advice.Argument(value = 1, optional = true) Object arg1) { + + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField virtualField = @@ -119,32 +113,17 @@ public static void startMethod( Context parentContext = Java8BytecodeBridge.currentContext(); String entityName = getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session)); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java index 16be5e006ad9..738cf85db6b1 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java @@ -13,13 +13,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -49,16 +48,10 @@ public void transform(TypeTransformer transformer) { public static class TransactionCommitAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startCommit( - @Advice.This Transaction transaction, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static HibernateOperationScope startCommit(@Advice.This Transaction transaction) { - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField transactionVirtualField = @@ -66,31 +59,17 @@ public static void startCommit( SessionInfo sessionInfo = transactionVirtualField.get(transaction); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = new HibernateOperation("Transaction.commit", sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } + HibernateOperation hibernateOperation = + new HibernateOperation("Transaction.commit", sessionInfo); - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endCommit( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/HibernateInstrumentationModule.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/HibernateInstrumentationModule.java index b8c8a34dd120..83af9e7b0f90 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/HibernateInstrumentationModule.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/HibernateInstrumentationModule.java @@ -11,11 +11,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class HibernateInstrumentationModule extends InstrumentationModule { +public class HibernateInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public HibernateInstrumentationModule() { super("hibernate", "hibernate-6.0"); @@ -29,10 +31,8 @@ public ElementMatcher.Junction classLoaderMatcher() { } @Override - public boolean isIndyModule() { - // shares classes with hibernate-procedure-call-4.3, these classes should be in the same class - // loader - return false; + public String getModuleGroup() { + return "hibernate"; } @Override diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java index 141175c30269..56ebecb8a2af 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java @@ -14,13 +14,12 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -64,16 +63,10 @@ public void transform(TypeTransformer transformer) { public static class QueryMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( - @Advice.This CommonQueryContract query, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + public static HibernateOperationScope startMethod(@Advice.This CommonQueryContract query) { + + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } String queryString = null; @@ -93,32 +86,17 @@ public static void startMethod( SessionInfo sessionInfo = queryVirtualField.get(query); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation(getOperationNameForQuery(queryString), sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java index fde18f963d42..6853916ca629 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java @@ -18,13 +18,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import jakarta.persistence.criteria.CriteriaQuery; import net.bytebuddy.asm.Advice; @@ -95,20 +94,15 @@ public void transform(TypeTransformer transformer) { public static class SessionMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( + public static HibernateOperationScope startMethod( @Advice.This SharedSessionContract session, @Advice.Origin("#m") String name, @Advice.Origin("#d") String descriptor, @Advice.Argument(0) Object arg0, - @Advice.Argument(value = 1, optional = true) Object arg1, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + @Advice.Argument(value = 1, optional = true) Object arg1) { + + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField virtualField = @@ -118,32 +112,17 @@ public static void startMethod( Context parentContext = Java8BytecodeBridge.currentContext(); String entityName = getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session)); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (callDepth.decrementAndGet() > 0) { - return; - } - - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java index 711773e4e5b4..cea7315139c2 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java @@ -13,13 +13,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -49,16 +48,10 @@ public void transform(TypeTransformer transformer) { public static class TransactionCommitAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startCommit( - @Advice.This Transaction transaction, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static HibernateOperationScope startCommit(@Advice.This Transaction transaction) { - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField transactionVirtualField = @@ -66,31 +59,17 @@ public static void startCommit( SessionInfo sessionInfo = transactionVirtualField.get(transaction); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = new HibernateOperation("Transaction.commit", sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } + HibernateOperation hibernateOperation = + new HibernateOperation("Transaction.commit", sessionInfo); - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endCommit( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperationScope.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperationScope.java new file mode 100644 index 000000000000..6c8831718da1 --- /dev/null +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperationScope.java @@ -0,0 +1,88 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.bootstrap.CallDepth; + +public class HibernateOperationScope { + + private final HibernateOperation hibernateOperation; + private final Context context; + private final Scope scope; + private final Instrumenter instrumenter; + + private HibernateOperationScope( + HibernateOperation hibernateOperation, + Context context, + Scope scope, + Instrumenter instrumenter) { + this.hibernateOperation = hibernateOperation; + this.context = context; + this.scope = scope; + this.instrumenter = instrumenter; + } + + /** + * Starts operation scope + * + * @param hibernateOperation hibernate operation + * @param parentContext parent context + * @param instrumenter instrumenter + * @return operation scope, to be ended with {@link #end(HibernateOperationScope, Throwable)} on + * exit advice. Might return {@literal null} when operation should not be captured. + */ + public static HibernateOperationScope start( + HibernateOperation hibernateOperation, + Context parentContext, + Instrumenter instrumenter) { + + if (!instrumenter.shouldStart(parentContext, hibernateOperation)) { + return null; + } + + Context context = instrumenter.start(parentContext, hibernateOperation); + + return new HibernateOperationScope( + hibernateOperation, context, context.makeCurrent(), instrumenter); + } + + /** + * Performs call depth increase and returns {@literal true} when depth is > 0, which indicates a + * nested hibernate operation is in progress. Must be called in the enter advice with an + * unconditional corresponding call to {@link #end(HibernateOperationScope, Throwable)} to + * decrement call depth. + */ + public static boolean enterDepthSkipCheck() { + CallDepth callDepth = CallDepth.forClass(HibernateOperation.class); + return callDepth.getAndIncrement() > 0; + } + + /** + * Ends operation scope. + * + * @param scope hibernate operation scope or {@literal null} when there is none + * @param throwable thrown exception + */ + public static void end(HibernateOperationScope scope, Throwable throwable) { + + CallDepth callDepth = CallDepth.forClass(HibernateOperation.class); + if (callDepth.decrementAndGet() > 0) { + return; + } + + if (scope != null) { + scope.end(throwable); + } + } + + private void end(Throwable throwable) { + scope.close(); + instrumenter.end(context, hibernateOperation, null, throwable); + } +} diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java index 11b04b34903e..3e914808d004 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java @@ -11,11 +11,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class HibernateInstrumentationModule extends InstrumentationModule { +public class HibernateInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public HibernateInstrumentationModule() { super("hibernate-procedure-call", "hibernate-procedure-call-4.3", "hibernate"); } @@ -26,10 +28,8 @@ public ElementMatcher.Junction classLoaderMatcher() { } @Override - public boolean isIndyModule() { - // uses SessionInfo class from hibernate common which is now in separate class loader for all - // instrumentations - return false; + public String getModuleGroup() { + return "hibernate"; } @Override diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java index ed9671808c2f..db6092567598 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java @@ -12,13 +12,12 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation; +import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperationScope; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -48,17 +47,11 @@ public void transform(TypeTransformer transformer) { public static class ProcedureCallMethodAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void startMethod( - @Advice.This ProcedureCall call, - @Advice.Origin("#m") String name, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static HibernateOperationScope startMethod( + @Advice.This ProcedureCall call, @Advice.Origin("#m") String name) { - callDepth = CallDepth.forClass(HibernateOperation.class); - if (callDepth.getAndIncrement() > 0) { - return; + if (HibernateOperationScope.enterDepthSkipCheck()) { + return null; } VirtualField criteriaVirtualField = @@ -66,32 +59,17 @@ public static void startMethod( SessionInfo sessionInfo = criteriaVirtualField.get(call); Context parentContext = Java8BytecodeBridge.currentContext(); - hibernateOperation = + HibernateOperation hibernateOperation = new HibernateOperation("ProcedureCall." + name, call.getProcedureName(), sessionInfo); - if (!instrumenter().shouldStart(parentContext, hibernateOperation)) { - return; - } - context = instrumenter().start(parentContext, hibernateOperation); - scope = context.makeCurrent(); + return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endMethod( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - - if (callDepth.decrementAndGet() > 0) { - return; - } + @Advice.Thrown Throwable throwable, @Advice.Enter HibernateOperationScope scope) { - if (scope != null) { - scope.close(); - instrumenter().end(context, hibernateOperation, null, throwable); - } + HibernateOperationScope.end(scope, throwable); } } } diff --git a/instrumentation/java-http-client/library/src/main/java/io/opentelemetry/instrumentation/httpclient/JavaHttpClientTelemetryBuilder.java b/instrumentation/java-http-client/library/src/main/java/io/opentelemetry/instrumentation/httpclient/JavaHttpClientTelemetryBuilder.java index 1e5cbd0c1490..66d8f753c300 100644 --- a/instrumentation/java-http-client/library/src/main/java/io/opentelemetry/instrumentation/httpclient/JavaHttpClientTelemetryBuilder.java +++ b/instrumentation/java-http-client/library/src/main/java/io/opentelemetry/instrumentation/httpclient/JavaHttpClientTelemetryBuilder.java @@ -95,7 +95,9 @@ public JavaHttpClientTelemetryBuilder setEmitExperimentalHttpClientMetrics( /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public JavaHttpClientTelemetryBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function< + SpanNameExtractor, + ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); return this; diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfServerSpanNaming.java b/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfServerSpanNaming.java deleted file mode 100644 index 96c5a74f16d2..000000000000 --- a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfServerSpanNaming.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.cxf; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan; -import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath; -import javax.servlet.http.HttpServletRequest; - -public final class CxfServerSpanNaming { - - public static void updateServerSpanName(Context context, CxfRequest cxfRequest) { - String spanName = cxfRequest.spanName(); - if (spanName == null) { - return; - } - - Span serverSpan = LocalRootSpan.fromContextOrNull(context); - if (serverSpan == null) { - return; - } - - HttpServletRequest request = (HttpServletRequest) cxfRequest.message().get("HTTP.REQUEST"); - if (request != null) { - String servletPath = request.getServletPath(); - if (!servletPath.isEmpty()) { - spanName = servletPath + "/" + spanName; - } - } - - serverSpan.updateName(ServletContextPath.prepend(context, spanName)); - } - - private CxfServerSpanNaming() {} -} diff --git a/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts index a434c6f65982..779f42848838 100644 --- a/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts @@ -11,7 +11,7 @@ dependencies { testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0:javaagent")) - testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent")) + testInstrumentation(project(":instrumentation:jaxws:jaxws-cxf-3.0:javaagent")) testInstrumentation(project(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")) } diff --git a/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts index 5d730662724a..62beaa709a54 100644 --- a/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0:javaagent")) - testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent")) + testInstrumentation(project(":instrumentation:jaxws:jaxws-cxf-3.0:javaagent")) testInstrumentation(project(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")) // wildfly version used to run tests diff --git a/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/build.gradle.kts new file mode 100644 index 000000000000..c6d53b53b53e --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + testLibrary("org.apache.cxf:cxf-rt-frontend-jaxws:4.0.0") + testLibrary("org.apache.cxf:cxf-rt-transports-http:4.0.0") + + testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0") + testImplementation(project(":instrumentation:jaxws:jaxws-3.0-common-testing")) + + testInstrumentation(project(":instrumentation:jaxws:jaxws-cxf-3.0:javaagent")) + + testInstrumentation(project(":instrumentation:servlet:servlet-5.0:javaagent")) + testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent")) +} + +otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_17) +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") +} diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/test/groovy/CxfJaxWsTest.groovy b/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/groovy/CxfJaxWsTest.groovy similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/test/groovy/CxfJaxWsTest.groovy rename to instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/groovy/CxfJaxWsTest.groovy diff --git a/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/groovy/TestWsServlet.groovy b/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/groovy/TestWsServlet.groovy new file mode 100644 index 000000000000..e68b2838d07c --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/groovy/TestWsServlet.groovy @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import hello.HelloServiceImpl +import io.opentelemetry.api.trace.Span +import io.opentelemetry.context.Context +import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan +import org.apache.cxf.jaxws.EndpointImpl +import org.apache.cxf.message.Message +import org.apache.cxf.phase.AbstractPhaseInterceptor +import org.apache.cxf.phase.Phase +import org.apache.cxf.transport.servlet.CXFNonSpringServlet + +import jakarta.servlet.ServletConfig + +class TestWsServlet extends CXFNonSpringServlet { + @Override + void loadBus(ServletConfig servletConfig) { + super.loadBus(servletConfig) + + // publish test webservice + Object implementor = new HelloServiceImpl() + EndpointImpl endpoint = new EndpointImpl(bus, implementor) + endpoint.publish("/HelloService") + endpoint.getOutInterceptors().add(new AbstractPhaseInterceptor(Phase.SETUP) { + @Override + void handleMessage(Message message) { + Context context = Context.current() + if (LocalRootSpan.fromContext(context) != Span.fromContext(context)) { + throw new IllegalStateException("handler span should be ended before outgoing interceptors") + } + } + }) + } +} diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/test/resources/test-app/WEB-INF/web.xml b/instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/resources/test-app/WEB-INF/web.xml similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/test/resources/test-app/WEB-INF/web.xml rename to instrumentation/jaxws/jaxws-3.0-cxf-4.0-testing/src/test/resources/test-app/WEB-INF/web.xml diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent-unit-tests/build.gradle.kts b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent-unit-tests/build.gradle.kts similarity index 78% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent-unit-tests/build.gradle.kts rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent-unit-tests/build.gradle.kts index d8fa5cc7c0cb..d41fa8e62010 100644 --- a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent-unit-tests/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent-unit-tests/build.gradle.kts @@ -6,7 +6,7 @@ dependencies { compileOnly("javax.servlet:javax.servlet-api:3.0.1") compileOnly("org.apache.cxf:cxf-rt-frontend-jaxws:3.0.0") - implementation(project(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent")) + implementation(project(":instrumentation:jaxws:jaxws-cxf-3.0:javaagent")) testImplementation(project(":instrumentation-api")) testImplementation("org.apache.cxf:cxf-rt-frontend-jaxws:3.0.0") diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptorTest.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptorTest.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptorTest.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptorTest.java diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/build.gradle.kts similarity index 90% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/build.gradle.kts index 0b1f54cb8239..5ed3ec89bbcf 100644 --- a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/build.gradle.kts @@ -8,8 +8,9 @@ muzzle { module.set("cxf-rt-frontend-jaxws") // all earlier versions in maven central also pass muzzle check, // but 3.0.0 is already 8 years old and testing earlier versions adds complexity - versions.set("[3.0.0,4)") + versions.set("[3.0.0,)") extraDependency("javax.servlet:javax.servlet-api:3.0.1") + extraDependency("jakarta.servlet:jakarta.servlet-api:5.0.0") } } @@ -18,6 +19,8 @@ dependencies { library("org.apache.cxf:cxf-rt-frontend-jaxws:3.0.0") compileOnly("javax.servlet:javax.servlet-api:3.0.1") + compileOnly("jakarta.servlet:jakarta.servlet-api:5.0.0") + compileOnly(project(":muzzle")) testLibrary("org.apache.cxf:cxf-rt-transports-http:3.0.0") diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfHelper.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfHelper.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfHelper.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfHelper.java diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfInstrumentationModule.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfInstrumentationModule.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfInstrumentationModule.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfInstrumentationModule.java diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfRequest.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfRequest.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfRequest.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfRequest.java diff --git a/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfServerSpanNaming.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfServerSpanNaming.java new file mode 100644 index 000000000000..4a5400f6e838 --- /dev/null +++ b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfServerSpanNaming.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.cxf; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan; +import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath; +import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle; + +public final class CxfServerSpanNaming { + private static final Class JAVAX_SERVLET_REQUEST = + loadClass("javax.servlet.http.HttpServletRequest"); + private static final Class JAKARTA_SERVLET_REQUEST = + loadClass("jakarta.servlet.http.HttpServletRequest"); + + private static Class loadClass(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException exception) { + return null; + } + } + + public static void updateServerSpanName(Context context, CxfRequest cxfRequest) { + String spanName = cxfRequest.spanName(); + if (spanName == null) { + return; + } + + Span serverSpan = LocalRootSpan.fromContextOrNull(context); + if (serverSpan == null) { + return; + } + + Object request = cxfRequest.message().get("HTTP.REQUEST"); + if (request != null) { + String servletPath = null; + if (JAVAX_SERVLET_REQUEST != null && JAVAX_SERVLET_REQUEST.isInstance(request)) { + servletPath = getJavaxServletPath(request); + } else if (JAKARTA_SERVLET_REQUEST != null && JAKARTA_SERVLET_REQUEST.isInstance(request)) { + servletPath = getJakartaServletPath(request); + } + if (servletPath != null && !servletPath.isEmpty()) { + spanName = servletPath + "/" + spanName; + } + } + + serverSpan.updateName(ServletContextPath.prepend(context, spanName)); + } + + @NoMuzzle + private static String getJavaxServletPath(Object request) { + return ((javax.servlet.http.HttpServletRequest) request).getServletPath(); + } + + @NoMuzzle + private static String getJakartaServletPath(Object request) { + return ((jakarta.servlet.http.HttpServletRequest) request).getServletPath(); + } + + private CxfServerSpanNaming() {} +} diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java similarity index 97% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java index 9da76ff3b41c..b048d97985b8 100644 --- a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java +++ b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java @@ -10,7 +10,7 @@ import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; public class CxfSingletons { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxws-2.0-cxf-3.0"; + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxws-cxf-3.0"; private static final Instrumenter INSTRUMENTER; diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/JaxWsServerFactoryBeanInstrumentation.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/JaxWsServerFactoryBeanInstrumentation.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/JaxWsServerFactoryBeanInstrumentation.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/JaxWsServerFactoryBeanInstrumentation.java diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingEndInInterceptor.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingEndInInterceptor.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingEndInInterceptor.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingEndInInterceptor.java diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingOutFaultInterceptor.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingOutFaultInterceptor.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingOutFaultInterceptor.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingOutFaultInterceptor.java diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptor.java b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptor.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptor.java rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/TracingStartInInterceptor.java diff --git a/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/groovy/CxfJaxWsTest.groovy b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/groovy/CxfJaxWsTest.groovy new file mode 100644 index 000000000000..b81fa377c198 --- /dev/null +++ b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/groovy/CxfJaxWsTest.groovy @@ -0,0 +1,7 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +class CxfJaxWsTest extends AbstractJaxWsTest { +} diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/test/groovy/TestWsServlet.groovy b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/groovy/TestWsServlet.groovy similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/test/groovy/TestWsServlet.groovy rename to instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/groovy/TestWsServlet.groovy diff --git a/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/resources/test-app/WEB-INF/web.xml b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/resources/test-app/WEB-INF/web.xml new file mode 100644 index 000000000000..9c6ebc21da06 --- /dev/null +++ b/instrumentation/jaxws/jaxws-cxf-3.0/javaagent/src/test/resources/test-app/WEB-INF/web.xml @@ -0,0 +1,17 @@ + + + + + wsServlet + TestWsServlet + 1 + + + + wsServlet + /ws/* + + diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java index f36ed7c24977..4301a3352588 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java @@ -19,5 +19,7 @@ public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { builder.ignoreClass("org.jboss.jca.adapters.jdbc."); // see https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8109 builder.ignoreClass("org.apache.shardingsphere.shardingjdbc.jdbc.core.statement."); + // see https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/12065 + builder.ignoreClass("org.apache.shardingsphere.driver.jdbc.core.statement."); } } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/JettyClientTelemetryBuilder.java b/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/JettyClientTelemetryBuilder.java index a914f73d266f..ec5c8b379d50 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/JettyClientTelemetryBuilder.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/JettyClientTelemetryBuilder.java @@ -112,7 +112,7 @@ public JettyClientTelemetryBuilder setEmitExperimentalHttpClientMetrics( /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public JettyClientTelemetryBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function, ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); return this; diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java index 427980f0d244..a976fe7a2f30 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java @@ -112,7 +112,7 @@ public JettyClientTelemetryBuilder setEmitExperimentalHttpClientMetrics( /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public JettyClientTelemetryBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function, ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); return this; diff --git a/instrumentation/jmx-metrics/javaagent/README.md b/instrumentation/jmx-metrics/javaagent/README.md index 8680e878814e..844137872f82 100644 --- a/instrumentation/jmx-metrics/javaagent/README.md +++ b/instrumentation/jmx-metrics/javaagent/README.md @@ -26,6 +26,7 @@ $ java -javaagent:path/to/opentelemetry-javaagent.jar \ No targets are enabled by default. The supported target environments are listed below. - [activemq](activemq.md) +- [camel](camel.md) - [jetty](jetty.md) - [kafka-broker](kafka-broker.md) - [tomcat](tomcat.md) diff --git a/instrumentation/jmx-metrics/javaagent/camel.md b/instrumentation/jmx-metrics/javaagent/camel.md new file mode 100644 index 000000000000..724fb488aa2c --- /dev/null +++ b/instrumentation/jmx-metrics/javaagent/camel.md @@ -0,0 +1,51 @@ +# Camel Metrics + +Here is the list of metrics based on MBeans exposed by Camel. + +| Metric Name | Type | Attributes | Description | +|------------------------------------------------|---------------|----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| camel.context.exchange | Counter | context, camelVersion | Indicates the total number of exchanges, passed or failed, processed since context start-up or the last reset operation. | +| camel.context.exchange.completed | Counter | context, camelVersion | Indicates the total number of exchanges processed successfully since context start-up or the last reset operation. | +| camel.context.exchange.failed | Counter | context, camelVersion | Indicates the total number of exchanges that failed to process since context start-up or the last reset operation. | +| camel.context.exchange.failed_handled | Counter | context, camelVersion | Indicates the number of exchanges failed and handled by an ExceptionHandler in the context. | +| camel.context.exchange.inflight | UpDownCounter | context, camelVersion | Indicates the number of exchanges currently transiting the context. | +| camel.context.exchange.processing.delta_time | Gauge | context, camelVersion | Indicates the difference, in milliseconds, of the Processing Time of the last two exchanges transited the context. | +| camel.context.exchange.processing.last_time | Gauge | context, camelVersion | Indicates the time, in milliseconds, it took to process the last exchange. | +| camel.context.exchange.processing.max_time | Gauge | context, camelVersion | Indicates the longest time, in milliseconds, to process an exchange since context start-up or the last reset operation. | +| camel.context.exchange.processing.mean_time | Gauge | context, camelVersion | Indicates the mean processing time, in milliseconds, for all exchanges processed since context start-up or the last reset operation. | +| camel.context.exchange.processing.min_time | Gauge | context, camelVersion | Indicates the shortest time, in milliseconds, to process an exchange since context start-up or the last reset operation. | +| camel.context.exchange.processing.time | Counter | context, camelVersion | Indicates the total processing time, in milliseconds, to process all exchanges since context start-up or the last reset operation. | +| camel.context.exchange.redelivered | Counter | context, camelVersion | Number of exchanges redelivered (internal only) since context start-up or the last reset operation. | +| camel.context.exchange.redelivered_external | Counter | context, camelVersion | The total number of all external initiated redeliveries (such as from JMS broker) since context start-up or the last reset operation. | +| camel.route.exchange | Counter | context, route | Indicates the total number of exchanges, passed or failed, that the route has processed since route start-up or the last reset operation. | +| camel.route.exchange.completed | Counter | context, route | Indicates the total number of exchanges the route has processed successfully since route start-up or the last reset operation. | +| camel.route.exchange.failed | Counter | context, route | Indicates the total number of exchanges that the route has failed to process since route start-up or the last reset operation. | +| camel.route.exchange.failed_handled | Counter | context, route | Indicates the number of exchanges failed and handled by an ExceptionHandler in the route. | +| camel.route.exchange.inflight | UpDownCounter | context, route | Indicates the number of exchanges currently transiting the route. | +| camel.route.exchange.processing.delta_time | Gauge | context, route | Indicates the difference, in milliseconds, of the Processing Time of the last two exchanges transited the route. | +| camel.route.exchange.processing.last_time | Gauge | context, route | Indicates the time, in milliseconds, it took the route to process the last exchange. | +| camel.route.exchange.processing.max_time | Gauge | context, route | Indicates the longest time, in milliseconds, to process an exchange since the route start-up or the last reset operation. | +| camel.route.exchange.processing.mean_time | Gauge | context, route | Indicates the mean processing time, in milliseconds, for all exchanges processed since the route start-up or the last reset operation. | +| camel.route.exchange.processing.min_time | Gauge | context, route | Indicates the shortest time, in milliseconds, to process an exchange since the route start-up or the last reset operation. | +| camel.route.exchange.processing.time | Counter | context, route | Indicates the total processing time, in milliseconds, of all exchanges the selected processed since route start-up or the last reset operation. | +| camel.route.exchange.redelivered | Counter | context, route | Number of exchanges redelivered (internal only) since route start-up or the last reset operation. | +| camel.route.exchange.redelivered_external | Counter | context, route | The total number of all external initiated redeliveries (such as from JMS broker) since the route start-up or the last reset operation. | +| camel.processor.exchange | Counter | context, route, processor, destination | Indicates the total number of exchanges, passed or failed, that the selected processor has processed since processor start-up or the last reset operation. | +| camel.processor.exchange.completed | Counter | context, route, processor, destination | Indicates the total number of exchanges the selected processor has processed successfully since processor start-up or the last reset operation. | +| camel.processor.exchange.failed | Counter | context, route, processor, destination | Indicates the total number of exchanges that the selected processor has failed to process since processor start-up or the last reset operation. | +| camel.processor.exchange.inflight | UpDownCounter | context, route, processor, destination | Indicates the number of exchanges currently transiting the processor. | +| camel.processor.exchange.failed_handled | Counter | context, route, processor, destination | Indicates the number of exchanges failed and handled by an ExceptionHandler in the context. | +| camel.processor.exchange.processing.delta_time | Gauge | context, route, processor, destination | Indicates the difference, in milliseconds, of the Processing Time of the last two exchanges transited the selected processor. | +| camel.processor.exchange.processing.last_time | Gauge | context, route, processor, destination | Indicates the time, in milliseconds, it took the selected processor to process the last exchange. | +| camel.processor.exchange.processing.max_time | Gauge | context, route, processor, destination | Indicates the longest time, in milliseconds, to process an exchange since processor start-up or the last reset operation. | +| camel.processor.exchange.processing.mean_time | Gauge | context, route, processor, destination | Indicates the mean processing time, in milliseconds, for all exchanges processed since processor start-up or the last reset operation. | +| camel.processor.exchange.processing.min_time | Gauge | context, route, processor, destination | Indicates the shortest time, in milliseconds, to process an exchange since processor start-up or the last reset operation. | +| camel.processor.exchange.processing.time | Counter | context, route, processor, destination | Indicates the total processing time, in milliseconds, to process all exchanges since start-up or the last reset operation. | +| camel.processor.exchange.redelivered | Counter | context, route, processor, destination | Number of exchanges redelivered (internal only) since selected processor start-up or the last reset operation. | +| camel.processor.exchange.redelivered_external | Counter | context, route, processor, destination | The total number of all external initiated redeliveries (such as from JMS broker) since processor start-up or the last reset operation. | +| camel.threadpool.active | UpDownCounter | context, route | The approximate number of threads that are actively executing tasks. | +| camel.threadpool.pool.size | UpDownCounter | context, route | The current number of threads in the pool. | +| camel.threadpool.pool.largest_size | Gauge | context, route | The largest number of threads that have ever simultaneously been in the pool. | +| camel.threadpool.task | Counter | context, route | The approximate total number of tasks that have ever been scheduled for execution. | +| camel.threadpool.task.completed | Counter | context, route | The approximate total number of tasks that have completed execution. Because the states of tasks and threads may change dynamically during computation, the returned value is only an approximation, but one that does not ever decrease across successive calls. | +| camel.threadpool.task.queue_size | UpDownCounter | context, route | The number of Tasks in the Task Queue. | diff --git a/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/camel.yaml b/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/camel.yaml new file mode 100644 index 000000000000..a34fa04a437f --- /dev/null +++ b/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/camel.yaml @@ -0,0 +1,263 @@ +--- +rules: + - bean: org.apache.camel:context=*,type=context,name=* + prefix: camel.context. + metricAttribute: + context: param(context) + camelVersion: beanattr(CamelVersion) + mapping: + ExchangesCompleted: + metric: exchange.completed + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges processed successfully since context start-up or the last reset operation. + ExchangesFailed: + metric: exchange.failed + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges that failed to process since context start-up or the last reset operation. + ExchangesInflight: + metric: exchange.inflight + type: updowncounter + unit: "{exchanges}" + desc: Indicates the number of exchanges currently transiting the context. + ExchangesTotal: + metric: exchange + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges, passed or failed, processed context start-up or the last reset operation. + FailuresHandled: + metric: exchange.failed_handled + unit: "{exchanges}" + type: counter + desc: Indicates the number of exchanges failed and handled by an ExceptionHandler in the context. + Redeliveries: + metric: exchange.redelivered + type: counter + unit: "{exchanges}" + desc: Number of exchanges redelivered (internal only) since context start-up or the last reset operation. + ExternalRedeliveries: + metric: exchange.redelivered_external + type: counter + unit: "{exchanges}" + desc: The total number of all external initiated redeliveries (such as from JMS broker) since context start-up or the last reset operation. + MaxProcessingTime: + metric: exchange.processing.max_time + unit: ms + type: gauge + desc: Indicates the longest time, in milliseconds, to process an exchange since context start-up or the last reset operation. + MeanProcessingTime: + metric: exchange.processing.mean_time + type: gauge + unit: ms + desc: Indicates the mean processing time, in milliseconds, for all exchanges processed since context start-up or the last reset operation. + MinProcessingTime: + metric: exchange.processing.min_time + unit: ms + type: gauge + desc: Indicates the shortest time, in milliseconds, to process an exchange since context start-up or the last reset operation. + LastProcessingTime: + metric: exchange.processing.last_time + unit: ms + type: gauge + desc: Indicates the time, in milliseconds, it took to process the last exchange. + DeltaProcessingTime: + metric: exchange.processing.delta_time + type: gauge + unit: ms + desc: Indicates the difference, in milliseconds, of the Processing Time of the last two exchanges transited the context. + TotalProcessingTime: + metric: exchange.processing.time + type: counter + unit: ms + desc: Indicates the total processing time, in milliseconds, to process all exchanges since context start-up or the last reset operation. + +# Route Level metrics + + - bean: org.apache.camel:context=*,type=routes,name=* + prefix: camel.route. + metricAttribute: + route: beanattr(RouteId) + context: param(context) + mapping: + ExchangesCompleted: + metric: exchange.completed + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges the route has processed successfully since route start-up or the last reset operation. + ExchangesFailed: + metric: exchange.failed + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges that the route has failed to process since route start-up or the last reset operation. + ExchangesInflight: + metric: exchange.inflight + type: updowncounter + unit: "{exchanges}" + desc: Indicates the number of exchanges currently transiting the route. + ExchangesTotal: + metric: exchange + unit: "{exchanges}" + type: counter + desc: Indicates the total number of exchanges, passed or failed, that the route has processed since route start-up or the last reset operation. + FailuresHandled: + metric: exchange.failed_handled + unit: "{exchanges}" + type: counter + desc: Indicates the number of exchanges failed and handled by an ExceptionHandler in the route. + ExternalRedeliveries: + metric: exchange.redelivered_external + unit: "{exchanges}" + type: counter + desc: The total number of all external initiated redeliveries (such as from JMS broker) since the route start-up or the last reset operation. + Redeliveries: + metric: exchange.redelivered + type: counter + unit: "{exchanges}" + desc: Number of exchanges redelivered (internal only) since route start-up or the last reset operation. + MaxProcessingTime: + metric: exchange.processing.max_time + unit: ms + type: gauge + desc: Indicates the longest time, in milliseconds, to process an exchange since the route start-up or the last reset operation. + MeanProcessingTime: + metric: exchange.processing.mean_time + unit: ms + type: gauge + desc: Indicates the mean processing time, in milliseconds, for all exchanges processed since the route start-up or the last reset operation. + MinProcessingTime: + metric: exchange.processing.min_time + type: gauge + unit: ms + desc: Indicates the shortest time, in milliseconds, to process an exchange since the route start-up or the last reset operation. + LastProcessingTime: + metric: exchange.processing.last_time + type: gauge + unit: ms + desc: Indicates the time, in milliseconds, it took the route to process the last exchange. + DeltaProcessingTime: + metric: exchange.processing.delta_time + type: gauge + unit: ms + desc: Indicates the difference, in milliseconds, of the Processing Time of the last two exchanges transited the route. + TotalProcessingTime: + metric: exchange.processing.time + type: counter + unit: ms + desc: Indicates the total processing time, in milliseconds, of all exchanges the selected processed since route start-up or the last reset operation. + + +# Processor level Metrics + + - bean: org.apache.camel:context=*,type=processors,name=* + prefix: camel.processor. + metricAttribute: + processor: beanattr(ProcessorId) + route: beanattr(RouteId) + context: param(context) + destination: beanattr(Destination) + mapping: + ExchangesCompleted: + metric: exchange.completed + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges the selected processor has processed successfully since processor start-up or the last reset operation. + ExchangesFailed: + metric: exchange.failed + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges that the selected processor has failed to process since processor start-up or the last reset operation. + ExchangesInflight: + metric: exchange.inflight + type: updowncounter + unit: "{exchanges}" + desc: Indicates the number of exchanges currently transiting the processor. + ExchangesTotal: + metric: exchange + type: counter + unit: "{exchanges}" + desc: Indicates the total number of exchanges, passed or failed, that the selected processor has processed since processor start-up or the last reset operation. + FailuresHandled: + metric: exchange.failed_handled + unit: "{exchanges}" + type: counter + desc: Indicates the number of exchanges failed and handled by an ExceptionHandler in the context. + ExternalRedeliveries: + metric: exchange.redelivered_external + type: counter + unit: "{exchanges}" + desc: The total number of all external initiated redeliveries (such as from JMS broker) since processor start-up or the last reset operation. + Redeliveries: + metric: exchange.redelivered + type: counter + unit: "{exchanges}" + desc: Number of exchanges redelivered (internal only) since selected processor start-up or the last reset operation. + MaxProcessingTime: + metric: exchange.processing.max_time + unit: ms + type: gauge + desc: Indicates the longest time, in milliseconds, to process an exchange since processor start-up or the last reset operation. + MeanProcessingTime: + metric: exchange.processing.mean_time + type: gauge + unit: ms + desc: Indicates the mean processing time, in milliseconds, for all exchanges processed since processor start-up or the last reset operation. + MinProcessingTime: + metric: exchange.processing.min_time + type: gauge + unit: ms + desc: Indicates the shortest time, in milliseconds, to process an exchange since processor start-up or the last reset operation. + LastProcessingTime: + metric: exchange.processing.last_time + type: gauge + unit: ms + desc: Indicates the time, in milliseconds, it took the selected processor to process the last exchange. + DeltaProcessingTime: + metric: exchange.processing.delta_time + type: gauge + unit: ms + desc: Indicates the difference, in milliseconds, of the Processing Time of the last two exchanges transited the selected processor. + TotalProcessingTime: + metric: exchange.processing.time + type: counter + unit: ms + desc: Indicates the total processing time, in milliseconds, to process all exchanges since start-up or the last reset operation. + + + + - bean: org.apache.camel:context=*,type=threadpools,name=* + prefix: camel.threadpool. + metricAttribute: + route: beanattr(RouteId) + context: param(context) + mapping: + ActiveCount: + metric: active + type: updowncounter + unit: "{threads}" + desc: The approximate number of threads that are actively executing tasks. + CompletedTaskCount: + metric: task.completed + type: counter + unit: "{tasks}" + desc: The approximate total number of tasks that have completed execution. Because the states of tasks and threads may change dynamically during computation, the returned value is only an approximation, but one that does not ever decrease across successive calls. + PoolSize: + metric: pool.size + type: updowncounter + unit: "{threads}" + desc: The current number of threads in the pool. + LargestPoolSize: + metric: pool.largest_size + type: gauge + unit: "{threads}" + desc: The largest number of threads that have ever simultaneously been in the pool. + TaskCount: + metric: task + type: counter + unit: "{tasks}" + desc: The approximate total number of tasks that have ever been scheduled for execution. + TaskQueueSize: + metric: task.queue_size + type: updowncounter + unit: "{threads}" + desc: The number of Tasks in the Task Queue. diff --git a/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java b/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java index 0853505ae98b..253583660ddd 100644 --- a/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java +++ b/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java @@ -28,6 +28,7 @@ class JmxMetricInsightInstallerTest { new HashSet<>( Arrays.asList( "activemq.yaml", + "camel.yaml", "hadoop.yaml", "jetty.yaml", "kafka-broker.yaml", diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java index f53fe5048387..141498e7836f 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java @@ -32,6 +32,7 @@ import java.util.Optional; import java.util.Random; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; @@ -70,6 +71,13 @@ public abstract class AbstractOpenTelemetryMetricsReporterTest { private static KafkaProducer producer; private static KafkaConsumer consumer; + private static final List metricsReporters = + new CopyOnWriteArrayList<>(); + + static { + OpenTelemetryMetricsReporter.setListener(metricsReporters::add); + } + @BeforeEach void beforeAll() { // only start the kafka container the first time this runs @@ -90,14 +98,16 @@ void beforeAll() { @AfterAll static void afterAll() { - kafka.stop(); producer.close(); consumer.close(); + kafka.stop(); } @AfterEach void tearDown() { - OpenTelemetryMetricsReporter.resetForTest(); + for (OpenTelemetryMetricsReporter metricsReporter : metricsReporters) { + metricsReporter.resetForTest(); + } } protected abstract InstrumentationExtension testing(); @@ -186,6 +196,14 @@ private static long countOpenTelemetryMetricsReporters(List met @Test void observeMetrics() { + // Firstly create new producer and consumer and close them. This is done tp verify that metrics + // are still produced after closing one producer/consumer. See + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/11880 + KafkaProducer producer2 = new KafkaProducer<>(producerConfig()); + KafkaConsumer consumer2 = new KafkaConsumer<>(consumerConfig()); + producer2.close(); + consumer2.close(); + produceRecords(); consumeRecords(); @@ -405,7 +423,9 @@ private static void printMappingTable() { Map> kafkaMetricsByGroup = TestMetricsReporter.seenMetrics.stream().collect(groupingBy(KafkaMetricId::getGroup)); List registeredObservables = - OpenTelemetryMetricsReporter.getRegisteredObservables(); + metricsReporters.stream() + .flatMap(metricsReporter -> metricsReporter.getRegisteredObservables().stream()) + .collect(toList()); // Iterate through groups in alpha order for (String group : kafkaMetricsByGroup.keySet().stream().sorted().collect(toList())) { List kafkaMetricIds = diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java index 1c98642c3a4b..f58aa345581b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java @@ -41,28 +41,35 @@ public final class OpenTelemetryMetricsReporter implements MetricsReporter { private static final Logger logger = Logger.getLogger(OpenTelemetryMetricsReporter.class.getName()); - private volatile Meter meter; + private static volatile Listener listener; - private static final Object lock = new Object(); + private volatile Meter meter; + private final Object lock = new Object(); @GuardedBy("lock") - private static final List registeredObservables = new ArrayList<>(); + private final List registeredObservables = new ArrayList<>(); /** * Reset for test by resetting the {@link #meter} to {@code null} and closing all registered * instruments. */ - static void resetForTest() { + void resetForTest() { closeAllInstruments(); } // Visible for test - static List getRegisteredObservables() { + List getRegisteredObservables() { synchronized (lock) { return new ArrayList<>(registeredObservables); } } + public OpenTelemetryMetricsReporter() { + if (listener != null) { + listener.metricsReporterCreated(this); + } + } + @Override public void init(List metrics) { metrics.forEach(this::metricChange); @@ -131,7 +138,7 @@ public void close() { closeAllInstruments(); } - private static void closeAllInstruments() { + private void closeAllInstruments() { synchronized (lock) { for (Iterator it = registeredObservables.iterator(); it.hasNext(); ) { closeInstrument(it.next().getObservable()); @@ -177,4 +184,14 @@ private static T getProperty(Map configs, String key, Class re } return (T) value; } + + // Visible for test + static void setListener(Listener listener) { + OpenTelemetryMetricsReporter.listener = listener; + } + + // used for testing + interface Listener { + void metricsReporterCreated(OpenTelemetryMetricsReporter metricsReporter); + } } diff --git a/instrumentation/ktor/ktor-2.0/javaagent/build.gradle.kts b/instrumentation/ktor/ktor-2.0/javaagent/build.gradle.kts index 130c152079fa..0c2fdd249e41 100644 --- a/instrumentation/ktor/ktor-2.0/javaagent/build.gradle.kts +++ b/instrumentation/ktor/ktor-2.0/javaagent/build.gradle.kts @@ -10,6 +10,7 @@ muzzle { group.set("org.jetbrains.kotlinx") module.set("ktor-server-core") versions.set("[2.0.0,)") + assertInverse.set(true) } } diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt index a98d77d60c15..421a642ac87d 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt @@ -16,6 +16,9 @@ import io.opentelemetry.context.Context import io.opentelemetry.context.propagation.ContextPropagators import io.opentelemetry.extension.kotlin.asContextElement import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.job +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class KtorClientTracing internal constructor( @@ -83,23 +86,25 @@ class KtorClientTracing internal constructor( } } + @OptIn(InternalCoroutinesApi::class) private fun installSpanEnd(plugin: KtorClientTracing, scope: HttpClient) { val endSpanPhase = PipelinePhase("OpenTelemetryEndSpan") scope.receivePipeline.insertPhaseBefore(HttpReceivePipeline.State, endSpanPhase) scope.receivePipeline.intercept(endSpanPhase) { val openTelemetryContext = it.call.attributes.getOrNull(openTelemetryContextKey) - - if (openTelemetryContext != null) { - try { - withContext(openTelemetryContext.asContextElement()) { proceed() } - plugin.endSpan(openTelemetryContext, it.call, null) - } catch (e: Throwable) { - plugin.endSpan(openTelemetryContext, it.call, e) - throw e + openTelemetryContext ?: return@intercept + + scope.launch { + val job = it.call.coroutineContext.job + job.join() + val cause = if (!job.isCancelled) { + null + } else { + kotlin.runCatching { job.getCancellationException() }.getOrNull() } - } else { - proceed() + + plugin.endSpan(openTelemetryContext, it.call, cause) } } } diff --git a/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/AbstractKtorHttpClientTest.kt b/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/AbstractKtorHttpClientTest.kt index 62e826e9d30d..bfdd14f5360c 100644 --- a/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/AbstractKtorHttpClientTest.kt +++ b/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/AbstractKtorHttpClientTest.kt @@ -10,15 +10,21 @@ import io.ktor.client.engine.cio.* import io.ktor.client.plugins.* import io.ktor.client.request.* import io.ktor.http.* +import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.context.Context import io.opentelemetry.extension.kotlin.asContextElement import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat +import io.opentelemetry.sdk.testing.assertj.TraceAssert +import io.opentelemetry.sdk.trace.data.StatusData import io.opentelemetry.semconv.NetworkAttributes import kotlinx.coroutines.* +import org.junit.jupiter.api.Test import java.net.URI +import java.util.function.Consumer abstract class AbstractKtorHttpClientTest : AbstractHttpClientTest() { @@ -71,4 +77,24 @@ abstract class AbstractKtorHttpClientTest : AbstractHttpClientTest + val span = trace.getSpan(0) + assertThat(span).hasKind(SpanKind.CLIENT).hasStatus(StatusData.unset()) + assertThat(span.endEpochNanos - span.startEpochNanos >= 1_000_000_000) + .describedAs("Span duration should be at least 1000ms") + .isTrue() + } + ) + } } diff --git a/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.java b/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.java index c74c918d355f..19e354bdf381 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.java +++ b/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.java @@ -6,11 +6,11 @@ package io.opentelemetry.instrumentation.lettuce.v5_1; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.assertj.core.api.Assertions.assertThat; import io.lettuce.core.api.sync.RedisCommands; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; @@ -90,7 +90,7 @@ void testAuthCommand() throws Exception { equalTo(ServerAttributes.SERVER_ADDRESS, host), equalTo(ServerAttributes.SERVER_PORT, port), equalTo(DbIncubatingAttributes.DB_SYSTEM, "redis"), - OpenTelemetryAssertions.satisfies( + satisfies( DbIncubatingAttributes.DB_STATEMENT, stringAssert -> stringAssert.startsWith("CLIENT SETINFO lib-ver")))), diff --git a/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.java b/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.java index 2492fb4c27ec..156a8b49df7f 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.java +++ b/instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.lettuce.v5_1; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -20,7 +21,6 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.test.utils.PortUtils; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; @@ -396,7 +396,7 @@ void testDebugSegfaultCommandWithNoArgumentProducesNoSpan() { equalTo(ServerAttributes.SERVER_ADDRESS, host), equalTo(ServerAttributes.SERVER_PORT, containerConnection.port), equalTo(DbIncubatingAttributes.DB_SYSTEM, "redis"), - OpenTelemetryAssertions.satisfies( + satisfies( DbIncubatingAttributes.DB_STATEMENT, stringAssert -> stringAssert.startsWith("CLIENT SETINFO lib-ver")))), diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/LogReplayOpenTelemetryAppenderTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/LogReplayOpenTelemetryAppenderTest.java index af14fe95a4bf..5b854d1fdc8d 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/LogReplayOpenTelemetryAppenderTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/LogReplayOpenTelemetryAppenderTest.java @@ -6,11 +6,10 @@ package io.opentelemetry.instrumentation.log4j.appender.v2_17; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.sdk.logs.data.LogRecordData; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import java.util.List; import org.apache.logging.log4j.message.StringMapMessage; import org.apache.logging.log4j.message.StructuredDataMessage; @@ -45,7 +44,7 @@ void twoLogs() { List logDataList = logRecordExporter.getFinishedLogRecordItems(); assertThat(logDataList).hasSize(1); - OpenTelemetryAssertions.assertThat(logDataList.get(0)) + assertThat(logDataList.get(0)) .hasResource(resource) .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("log message 1"); @@ -69,7 +68,7 @@ void twoLogsStringMapMessage() { List logDataList = logRecordExporter.getFinishedLogRecordItems(); assertThat(logDataList).hasSize(1); - OpenTelemetryAssertions.assertThat(logDataList.get(0)) + assertThat(logDataList.get(0)) .hasResource(resource) .hasInstrumentationScope(instrumentationScopeInfo) .hasAttributesSatisfyingExactly( @@ -94,7 +93,7 @@ void twoLogsStructuredDataMessage() { List logDataList = logRecordExporter.getFinishedLogRecordItems(); assertThat(logDataList).hasSize(1); - OpenTelemetryAssertions.assertThat(logDataList.get(0)) + assertThat(logDataList.get(0)) .hasResource(resource) .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("a message") diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/README.md b/instrumentation/logback/logback-appender-1.0/javaagent/README.md index aaa7be6e0364..2a962ae4e739 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/README.md +++ b/instrumentation/logback/logback-appender-1.0/javaagent/README.md @@ -7,6 +7,7 @@ | `otel.instrumentation.logback-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. | | `otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. | | `otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. | +| `otel.instrumentation.logback-appender.experimental.capture-arguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `otel.instrumentation.logback-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | [source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java index b2b9eb8a5a92..a3d1c6d90688 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java @@ -36,6 +36,9 @@ public final class LogbackSingletons { config.getBoolean( "otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes", false); + boolean captureArguments = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental.capture-arguments", false); List captureMdcAttributes = config.getList( "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", @@ -49,6 +52,7 @@ public final class LogbackSingletons { .setCaptureMarkerAttribute(captureMarkerAttribute) .setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes) .setCaptureLoggerContext(captureLoggerContext) + .setCaptureArguments(captureArguments) .build(); } diff --git a/instrumentation/logback/logback-appender-1.0/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index a002e3a704a0..14c515071d76 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -100,6 +100,7 @@ The available settings are: | `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. | | `captureKeyValuePairAttributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. | | `captureLoggerContext` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. | +| `captureArguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | | `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. thread.id attribute is not captured. | diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java index 3d689e7a5eb5..06904c219c81 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java @@ -33,6 +33,7 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase captureMdcAttributes = emptyList(); private volatile OpenTelemetry openTelemetry; @@ -79,6 +80,7 @@ public void start() { .setCaptureMarkerAttribute(captureMarkerAttribute) .setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes) .setCaptureLoggerContext(captureLoggerContext) + .setCaptureArguments(captureArguments) .build(); eventsToReplay = new ArrayBlockingQueue<>(numLogsCapturedBeforeOtelInstall); super.start(); @@ -164,6 +166,15 @@ public void setCaptureLoggerContext(boolean captureLoggerContext) { this.captureLoggerContext = captureLoggerContext; } + /** + * Sets whether the arguments should be set to logs. + * + * @param captureArguments To enable or disable capturing logger arguments + */ + public void setCaptureArguments(boolean captureArguments) { + this.captureArguments = captureArguments; + } + /** Configures the {@link MDC} attributes that will be copied to logs. */ public void setCaptureMdcAttributes(String attributes) { if (attributes != null) { diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index 5ff92f6672b7..8f0af6048202 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -24,9 +24,11 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; @@ -45,6 +47,7 @@ public final class LoggingEventMapper { private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); + private static final boolean supportsInstant = supportsInstant(); private static final boolean supportsKeyValuePairs = supportsKeyValuePairs(); private static final boolean supportsMultipleMarkers = supportsMultipleMarkers(); private static final Cache> mdcAttributeKeys = Cache.bounded(100); @@ -52,6 +55,10 @@ public final class LoggingEventMapper { private static final AttributeKey> LOG_MARKER = AttributeKey.stringArrayKey("logback.marker"); + private static final AttributeKey LOG_BODY_TEMPLATE = + AttributeKey.stringKey("log.body.template"); + private static final AttributeKey> LOG_BODY_PARAMETERS = + AttributeKey.stringArrayKey("log.body.parameters"); private final boolean captureExperimentalAttributes; private final List captureMdcAttributes; @@ -60,6 +67,7 @@ public final class LoggingEventMapper { private final boolean captureMarkerAttribute; private final boolean captureKeyValuePairAttributes; private final boolean captureLoggerContext; + private final boolean captureArguments; private LoggingEventMapper(Builder builder) { this.captureExperimentalAttributes = builder.captureExperimentalAttributes; @@ -68,6 +76,7 @@ private LoggingEventMapper(Builder builder) { this.captureMarkerAttribute = builder.captureMarkerAttribute; this.captureKeyValuePairAttributes = builder.captureKeyValuePairAttributes; this.captureLoggerContext = builder.captureLoggerContext; + this.captureArguments = builder.captureArguments; this.captureAllMdcAttributes = builder.captureMdcAttributes.size() == 1 && builder.captureMdcAttributes.get(0).equals("*"); } @@ -106,8 +115,12 @@ private void mapLoggingEvent( } // time - long timestamp = loggingEvent.getTimeStamp(); - builder.setTimestamp(timestamp, TimeUnit.MILLISECONDS); + if (supportsInstant && hasInstant(loggingEvent)) { + setTimestampFromInstant(builder, loggingEvent); + } else { + long timestamp = loggingEvent.getTimeStamp(); + builder.setTimestamp(timestamp, TimeUnit.MILLISECONDS); + } // level Level level = loggingEvent.getLevel(); @@ -168,12 +181,40 @@ private void mapLoggingEvent( captureLoggerContext(attributes, loggingEvent.getLoggerContextVO().getPropertyMap()); } + if (captureArguments + && loggingEvent.getArgumentArray() != null + && loggingEvent.getArgumentArray().length > 0) { + captureArguments(attributes, loggingEvent.getMessage(), loggingEvent.getArgumentArray()); + } + builder.setAllAttributes(attributes.build()); // span context builder.setContext(Context.current()); } + // getInstant is available since Logback 1.3 + private static boolean supportsInstant() { + try { + ILoggingEvent.class.getMethod("getInstant"); + } catch (NoSuchMethodException e) { + return false; + } + + return true; + } + + @NoMuzzle + private static boolean hasInstant(ILoggingEvent loggingEvent) { + return loggingEvent.getInstant() != null; + } + + @NoMuzzle + private static void setTimestampFromInstant( + LogRecordBuilder builder, ILoggingEvent loggingEvent) { + builder.setTimestamp(loggingEvent.getInstant()); + } + // visible for testing void captureMdcAttributes(AttributesBuilder attributes, Map mdcProperties) { if (captureAllMdcAttributes) { @@ -191,6 +232,13 @@ void captureMdcAttributes(AttributesBuilder attributes, Map mdcP } } + void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { + attributes.put(LOG_BODY_TEMPLATE, message); + attributes.put( + LOG_BODY_PARAMETERS, + Arrays.stream(arguments).map(String::valueOf).collect(Collectors.toList())); + } + public static AttributeKey getMdcAttributeKey(String key) { return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); } @@ -231,19 +279,20 @@ private static void captureKeyValuePairAttributes( if (keyValuePairs != null) { for (KeyValuePair keyValuePair : keyValuePairs) { Object value = keyValuePair.value; - if (keyValuePair.value != null) { + if (value != null) { + String key = keyValuePair.key; // preserve type for boolean and numeric values, everything else is converted to String if (value instanceof Boolean) { - attributes.put(keyValuePair.key, (Boolean) keyValuePair.value); + attributes.put(key, (Boolean) value); } else if (value instanceof Byte || value instanceof Integer || value instanceof Long || value instanceof Short) { - attributes.put(keyValuePair.key, ((Number) keyValuePair.value).longValue()); + attributes.put(key, ((Number) value).longValue()); } else if (value instanceof Double || value instanceof Float) { - attributes.put(keyValuePair.key, ((Number) keyValuePair.value).doubleValue()); + attributes.put(key, ((Number) value).doubleValue()); } else { - attributes.put(getAttributeKey(keyValuePair.key), keyValuePair.value.toString()); + attributes.put(getAttributeKey(key), value.toString()); } } } @@ -331,6 +380,7 @@ public static final class Builder { private boolean captureMarkerAttribute; private boolean captureKeyValuePairAttributes; private boolean captureLoggerContext; + private boolean captureArguments; Builder() {} @@ -370,6 +420,12 @@ public Builder setCaptureLoggerContext(boolean captureLoggerContext) { return this; } + @CanIgnoreReturnValue + public Builder setCaptureArguments(boolean captureArguments) { + this.captureArguments = captureArguments; + return this; + } + public LoggingEventMapper build() { return new LoggingEventMapper(this); } diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java index 79d3c507295d..af17aeaedd84 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java @@ -112,4 +112,43 @@ void multipleMarkers() { AttributeKey.stringArrayKey("logback.marker"), value -> assertThat(value).isEqualTo(Arrays.asList(markerName1, markerName2))); } + + @Test + void arguments() { + logger + .atInfo() + .setMessage("log message {} and {}, bool {}, long {}") + .addArgument("'world'") + .addArgument(Math.PI) + .addArgument(true) + .addArgument(Long.MAX_VALUE) + .log(); + + List logDataList = logRecordExporter.getFinishedLogRecordItems(); + assertThat(logDataList).hasSize(1); + LogRecordData logData = logDataList.get(0); + + assertThat(logData.getResource()).isEqualTo(resource); + assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); + assertThat(logData.getBody().asString()) + .isEqualTo( + "log message 'world' and 3.141592653589793, bool true, long 9223372036854775807"); + assertThat(logData.getAttributes().size()).isEqualTo(6); + assertThat(logData.getAttributes()) + .hasEntrySatisfying( + AttributeKey.stringArrayKey("log.body.parameters"), + value -> + assertThat(value) + .isEqualTo( + Arrays.asList( + "'world'", + String.valueOf(Math.PI), + String.valueOf(true), + String.valueOf(Long.MAX_VALUE)))); + assertThat(logData) + .hasAttributesSatisfying( + equalTo( + AttributeKey.stringKey("log.body.template"), + "log message {} and {}, bool {}, long {}")); + } } diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml index d02bf772803d..366678be3369 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml @@ -14,6 +14,7 @@ true true true + true * diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java index 39244e330122..7595d9ddd22d 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java @@ -113,6 +113,7 @@ void logWithExtras() { executeAfterLogsExecution(); + Instant now = Instant.now(); List logDataList = logRecordExporter.getFinishedLogRecordItems(); assertThat(logDataList).hasSize(1); LogRecordData logData = logDataList.get(0); @@ -121,7 +122,7 @@ void logWithExtras() { assertThat(logData.getBody().asString()).isEqualTo("log message 1"); assertThat(logData.getTimestampEpochNanos()) .isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(start.toEpochMilli())) - .isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(Instant.now().toEpochMilli())); + .isLessThanOrEqualTo(TimeUnit.SECONDS.toNanos(now.getEpochSecond()) + now.getNano()); assertThat(logData.getSeverity()).isEqualTo(Severity.INFO); assertThat(logData.getSeverityText()).isEqualTo("INFO"); assertThat(logData.getAttributes().size()) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogReplayOpenTelemetryAppenderTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogReplayOpenTelemetryAppenderTest.java index 50625dcd14ef..778fcef42839 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogReplayOpenTelemetryAppenderTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogReplayOpenTelemetryAppenderTest.java @@ -5,13 +5,12 @@ package io.opentelemetry.instrumentation.logback.appender.v1_0; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.util.ContextInitializer; import ch.qos.logback.core.spi.ContextAware; import io.opentelemetry.sdk.logs.data.LogRecordData; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import java.net.URL; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -65,7 +64,7 @@ void twoLogs() { List logDataList = logRecordExporter.getFinishedLogRecordItems(); assertThat(logDataList).hasSize(1); LogRecordData logData = logDataList.get(0); - OpenTelemetryAssertions.assertThat(logData) + assertThat(logData) .hasResource(resource) .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("log message 1") diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java index 50bbc17664de..126101bd1caa 100644 --- a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java +++ b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java @@ -79,6 +79,9 @@ public static void stopSpan( @Advice.Local("otelScope") Scope scope, @Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue, @Advice.Thrown Throwable throwable) { + if (scope == null) { + return; + } scope.close(); returnValue = diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java index ae09fe1377e7..6625cfbdb322 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java @@ -56,55 +56,54 @@ public void transform(TypeTransformer transformer) { public static class ChannelConnectAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.This Channel channel, - @Advice.Argument(0) SocketAddress remoteAddress, - @Advice.Local("otelParentContext") Context parentContext, - @Advice.Local("otelRequest") NettyConnectionRequest request, - @Advice.Local("otelTimer") Timer timer) { - - parentContext = Java8BytecodeBridge.currentContext(); + public static NettyScope onEnter( + @Advice.This Channel channel, @Advice.Argument(0) SocketAddress remoteAddress) { + + Context parentContext = Java8BytecodeBridge.currentContext(); Span span = Java8BytecodeBridge.spanFromContext(parentContext); if (!span.getSpanContext().isValid()) { - return; + return null; } VirtualField virtualField = VirtualField.find(Channel.class, NettyConnectionContext.class); if (virtualField.get(channel) != null) { - return; + return null; } virtualField.set(channel, new NettyConnectionContext(parentContext)); - request = NettyConnectionRequest.connect(remoteAddress); - timer = Timer.start(); + NettyConnectionRequest request = NettyConnectionRequest.connect(remoteAddress); + Timer timer = Timer.start(); + + return new NettyScope(parentContext, request, timer); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onExit( @Advice.Return ChannelFuture channelFuture, @Advice.Thrown Throwable error, - @Advice.Local("otelParentContext") Context parentContext, - @Advice.Local("otelRequest") NettyConnectionRequest request, - @Advice.Local("otelTimer") Timer timer) { + @Advice.Enter NettyScope nettyScope) { - if (request == null) { + if (nettyScope == null) { return; } if (error != null) { - if (connectionInstrumenter().shouldStart(parentContext, request)) { + if (connectionInstrumenter() + .shouldStart(nettyScope.getParentContext(), nettyScope.getRequest())) { InstrumenterUtil.startAndEnd( connectionInstrumenter(), - parentContext, - request, + nettyScope.getParentContext(), + nettyScope.getRequest(), null, error, - timer.startTime(), - timer.now()); + nettyScope.getTimer().startTime(), + nettyScope.getTimer().now()); } } else { - channelFuture.addListener(new ConnectionListener(parentContext, request, timer)); + channelFuture.addListener( + new ConnectionListener( + nettyScope.getParentContext(), nettyScope.getRequest(), nettyScope.getTimer())); } } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java index 0c9b546f7cb1..f5ef00b0ba0c 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java @@ -51,25 +51,25 @@ public void transform(TypeTransformer transformer) { public static class ChannelPipelineAdd2ArgsAdvice { @Advice.OnMethodEnter - public static void checkDepth( - @Advice.This ChannelPipeline pipeline, - @Advice.Argument(1) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + public static CallDepth checkDepth( + @Advice.This ChannelPipeline pipeline, @Advice.Argument(1) ChannelHandler handler) { // Pipelines are created once as a factory and then copied multiple times using the same add // methods as we are hooking. If our handler has already been added we need to remove it so we // don't end up with duplicates (this throws an exception) if (pipeline.get(handler.getClass().getName()) != null) { pipeline.remove(handler.getClass().getName()); } - callDepth = CallDepth.forClass(ChannelPipeline.class); + CallDepth callDepth = CallDepth.forClass(ChannelPipeline.class); callDepth.getAndIncrement(); + return callDepth; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void addHandler( @Advice.This ChannelPipeline pipeline, @Advice.Argument(1) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Enter CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { return; } @@ -82,29 +82,28 @@ public static void addHandler( public static class ChannelPipelineAdd3ArgsAdvice { @Advice.OnMethodEnter - public static void checkDepth( - @Advice.This ChannelPipeline pipeline, - @Advice.Argument(2) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + public static CallDepth checkDepth( + @Advice.This ChannelPipeline pipeline, @Advice.Argument(2) ChannelHandler handler) { // Pipelines are created once as a factory and then copied multiple times using the same add // methods as we are hooking. If our handler has already been added we need to remove it so we // don't end up with duplicates (this throws an exception) if (pipeline.get(handler.getClass().getName()) != null) { pipeline.remove(handler.getClass().getName()); } - callDepth = CallDepth.forClass(ChannelPipeline.class); + CallDepth callDepth = CallDepth.forClass(ChannelPipeline.class); callDepth.getAndIncrement(); + return callDepth; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void addHandler( @Advice.This ChannelPipeline pipeline, @Advice.Argument(2) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Enter CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { return; } - ChannelPipelineUtil.wrapHandler(pipeline, handler); } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyInstrumentationModule.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyInstrumentationModule.java index 54b65990b13f..a6cb34099421 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyInstrumentationModule.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyInstrumentationModule.java @@ -11,11 +11,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class NettyInstrumentationModule extends InstrumentationModule { +public class NettyInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public NettyInstrumentationModule() { super("netty", "netty-3.8"); } @@ -25,6 +27,11 @@ public ElementMatcher.Junction classLoaderMatcher() { return hasClassesNamed("org.jboss.netty.handler.codec.http.HttpMessage"); } + @Override + public String getModuleGroup() { + return "netty"; + } + @Override public List typeInstrumentations() { return asList( diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyScope.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyScope.java new file mode 100644 index 000000000000..56d4a34ecef7 --- /dev/null +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyScope.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v3_8; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.Timer; + +/** Container used to carry state between enter and exit advices */ +public class NettyScope { + + private final Context parentContext; + private final NettyConnectionRequest request; + private final Timer timer; + + public NettyScope(Context parentContext, NettyConnectionRequest request, Timer timer) { + this.parentContext = parentContext; + this.request = request; + this.timer = timer; + } + + public Context getParentContext() { + return parentContext; + } + + public NettyConnectionRequest getRequest() { + return request; + } + + public Timer getTimer() { + return timer; + } +} diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java index 0abc18d2aa31..155927b915fb 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java @@ -22,6 +22,7 @@ import java.util.Iterator; import java.util.Map; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -154,11 +155,13 @@ public static void removeHandler( public static class RemoveLastAdvice { @Advice.OnMethodExit(suppress = Throwable.class) - public static void removeHandler( - @Advice.This ChannelPipeline pipeline, - @Advice.Return(readOnly = false) ChannelHandler handler) { + @Advice.AssignReturned.ToReturned + public static ChannelHandler removeHandler( + @Advice.This ChannelPipeline pipeline, @Advice.Return ChannelHandler returnHandler) { VirtualField virtualField = VirtualField.find(ChannelHandler.class, ChannelHandler.class); + // TODO remove this extra variable when migrating to "indy only" instrumentation. + ChannelHandler handler = returnHandler; ChannelHandler ourHandler = virtualField.get(handler); if (ourHandler != null) { // Context is null when our handler has already been removed. This happens when calling @@ -176,6 +179,7 @@ public static void removeHandler( handler = pipeline.removeLast(); } } + return handler; } } @@ -183,9 +187,14 @@ public static void removeHandler( public static class AddAfterAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void addAfterHandler( - @Advice.This ChannelPipeline pipeline, - @Advice.Argument(value = 1, readOnly = false) String name) { + @Advice.AssignReturned.ToArguments(@ToArgument(1)) + public static String addAfterHandler( + @Advice.This ChannelPipeline pipeline, @Advice.Argument(value = 1) String nameArg) { + // TODO remove this extra variable when migrating to "indy only" instrumentation. + // using an intermediate variable is required to keep the advice work with "inlined" and + // "indy" this is probably a minor side-effect of using @Advice.AssignReturned.ToArguments + // with and inlined advice. + String name = nameArg; ChannelHandler handler = pipeline.get(name); if (handler != null) { VirtualField virtualField = @@ -195,6 +204,7 @@ public static void addAfterHandler( name = ourHandler.getClass().getName(); } } + return name; } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyFutureInstrumentation.java b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyFutureInstrumentation.java index 49a0b86756bf..697cca48f05e 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyFutureInstrumentation.java +++ b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyFutureInstrumentation.java @@ -19,6 +19,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -58,22 +59,30 @@ public void transform(TypeTransformer transformer) { public static class AddListenerAdvice { @Advice.OnMethodEnter - public static void wrapListener( - @Advice.Argument(value = 0, readOnly = false) - GenericFutureListener> listener) { + @Advice.AssignReturned.ToArguments(@ToArgument(0)) + public static GenericFutureListener> wrapListener( + @Advice.Argument(value = 0) GenericFutureListener> listenerArg) { + + // TODO remove this extra variable when migrating to "indy only" instrumentation. + GenericFutureListener> listener = listenerArg; if (FutureListenerWrappers.shouldWrap(listener)) { listener = FutureListenerWrappers.wrap(Java8BytecodeBridge.currentContext(), listener); } + return listener; } } @SuppressWarnings("unused") public static class AddListenersAdvice { + // here the AsScalar allows to assign the value of the returned array to the argument value, + // otherwise it's considered to be an Object[] that contains the arguments/return value/thrown + // exception assignments that bytebuddy has to do after the advice is invoked. @Advice.OnMethodEnter - public static void wrapListener( - @Advice.Argument(value = 0, readOnly = false) - GenericFutureListener>[] listeners) { + @Advice.AssignReturned.AsScalar + @Advice.AssignReturned.ToArguments(@ToArgument(0)) + public static Object[] wrapListener( + @Advice.Argument(value = 0) GenericFutureListener>[] listeners) { Context context = Java8BytecodeBridge.currentContext(); @SuppressWarnings({"unchecked", "rawtypes"}) @@ -86,7 +95,7 @@ public static void wrapListener( wrappedListeners[i] = listeners[i]; } } - listeners = wrappedListeners; + return wrappedListeners; } } @@ -94,20 +103,24 @@ public static void wrapListener( public static class RemoveListenerAdvice { @Advice.OnMethodEnter - public static void wrapListener( - @Advice.Argument(value = 0, readOnly = false) - GenericFutureListener> listener) { - listener = FutureListenerWrappers.getWrapper(listener); + @Advice.AssignReturned.ToArguments(@ToArgument(0)) + public static GenericFutureListener> wrapListener( + @Advice.Argument(value = 0) GenericFutureListener> listener) { + return FutureListenerWrappers.getWrapper(listener); } } @SuppressWarnings("unused") public static class RemoveListenersAdvice { + // here the AsScalar allows to assign the value of the returned array to the argument value, + // otherwise it's considered to be an Object[] that contains the arguments/return value/thrown + // exception assignments that bytebuddy has to do after the advice is invoked. @Advice.OnMethodEnter - public static void wrapListener( - @Advice.Argument(value = 0, readOnly = false) - GenericFutureListener>[] listeners) { + @Advice.AssignReturned.AsScalar + @Advice.AssignReturned.ToArguments(@ToArgument(0)) + public static Object[] wrapListener( + @Advice.Argument(value = 0) GenericFutureListener>[] listeners) { @SuppressWarnings({"unchecked", "rawtypes"}) GenericFutureListener>[] wrappedListeners = @@ -115,7 +128,7 @@ public static void wrapListener( for (int i = 0; i < listeners.length; ++i) { wrappedListeners[i] = FutureListenerWrappers.getWrapper(listeners[i]); } - listeners = wrappedListeners; + return wrappedListeners; } } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyScope.java b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyScope.java new file mode 100644 index 000000000000..94b7f997b24f --- /dev/null +++ b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/NettyScope.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v4.common; + +import io.netty.channel.ChannelPromise; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyConnectionInstrumenter; + +/** Container used to carry state between enter and exit advices */ +public class NettyScope { + + private final Context context; + private final NettyConnectionRequest request; + private final Scope scope; + + private NettyScope(Context context, NettyConnectionRequest request, Scope scope) { + this.context = context; + this.request = request; + this.scope = scope; + } + + public static NettyScope startNew( + NettyConnectionInstrumenter instrumenter, + Context parentContext, + NettyConnectionRequest request) { + Context context = instrumenter.start(parentContext, request); + return new NettyScope(context, request, context.makeCurrent()); + } + + public static void end( + NettyScope nettyScope, + NettyConnectionInstrumenter instrumenter, + ChannelPromise channelPromise, + Throwable throwable) { + + if (nettyScope == null) { + return; + } + + nettyScope.scope.close(); + + if (throwable != null) { + instrumenter.end(nettyScope.context, nettyScope.request, null, throwable); + } else { + channelPromise.addListener( + new ConnectionCompleteListener(instrumenter, nettyScope.context, nettyScope.request)); + } + } +} diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java index e7260ed353be..37dcd6cfcd0c 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java @@ -11,12 +11,11 @@ import io.netty.channel.ChannelPromise; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; -import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyScope; import java.net.SocketAddress; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -40,42 +39,24 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ConnectAdvice { @Advice.OnMethodEnter - public static void startConnect( - @Advice.Argument(2) SocketAddress remoteAddress, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelRequest") NettyConnectionRequest request, - @Advice.Local("otelScope") Scope scope) { + public static NettyScope startConnect(@Advice.Argument(2) SocketAddress remoteAddress) { Context parentContext = Java8BytecodeBridge.currentContext(); - request = NettyConnectionRequest.connect(remoteAddress); + NettyConnectionRequest request = NettyConnectionRequest.connect(remoteAddress); if (!connectionInstrumenter().shouldStart(parentContext, request)) { - return; + return null; } - - context = connectionInstrumenter().start(parentContext, request); - scope = context.makeCurrent(); + return NettyScope.startNew(connectionInstrumenter(), parentContext, request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endConnect( @Advice.Thrown Throwable throwable, @Advice.Argument(4) ChannelPromise channelPromise, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelRequest") NettyConnectionRequest request, - @Advice.Local("otelScope") Scope scope) { - - if (scope == null) { - return; - } - scope.close(); + @Advice.Enter NettyScope enterScope) { - if (throwable != null) { - connectionInstrumenter().end(context, request, null, throwable); - } else { - channelPromise.addListener( - new ConnectionCompleteListener(connectionInstrumenter(), context, request)); - } + NettyScope.end(enterScope, connectionInstrumenter(), channelPromise, throwable); } } } diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java index d913369e31fd..0db21d84da39 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java @@ -54,16 +54,18 @@ public void transform(TypeTransformer transformer) { public static class ChannelPipelineAddAdvice { @Advice.OnMethodEnter - public static void trackCallDepth(@Advice.Local("otelCallDepth") CallDepth callDepth) { - callDepth = CallDepth.forClass(ChannelPipeline.class); + public static CallDepth trackCallDepth() { + CallDepth callDepth = CallDepth.forClass(ChannelPipeline.class); callDepth.getAndIncrement(); + return callDepth; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void addHandler( @Advice.This ChannelPipeline pipeline, @Advice.Argument(2) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Enter CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { return; } diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyInstrumentationModule.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyInstrumentationModule.java index 1d2151795892..a20246a5d1e3 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyInstrumentationModule.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyInstrumentationModule.java @@ -12,12 +12,14 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyFutureInstrumentation; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class NettyInstrumentationModule extends InstrumentationModule { +public class NettyInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public NettyInstrumentationModule() { super("netty", "netty-4.0"); } @@ -31,6 +33,11 @@ public ElementMatcher.Junction classLoaderMatcher() { not(hasClassesNamed("io.netty.handler.codec.http.CombinedHttpHeaders"))); } + @Override + public String getModuleGroup() { + return "netty"; + } + @Override public List typeInstrumentations() { return asList( diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java index ae5f3e0f43eb..474701939f06 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java @@ -18,9 +18,9 @@ import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import java.util.Deque; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -61,8 +61,7 @@ public static void onEnter( instrumenter().end(clientContext, request, null, throwable); return; } - Deque serverContexts = ctx.channel().attr(AttributeKeys.SERVER_CONTEXT).get(); - ServerContext serverContext = serverContexts != null ? serverContexts.peekFirst() : null; + ServerContext serverContext = ServerContexts.peekFirst(ctx.channel()); if (serverContext != null) { NettyErrorHolder.set(serverContext.context(), throwable); } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java index aa91de4d1bb3..2801c847eb5a 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java @@ -16,14 +16,14 @@ import io.netty.resolver.AddressResolverGroup; import io.netty.resolver.DefaultAddressResolverGroup; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; -import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyScope; import java.net.SocketAddress; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -66,50 +66,34 @@ public static void onExit(@Advice.This Bootstrap bootstrap) { public static class SetResolverAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.Argument(value = 0, readOnly = false) AddressResolverGroup resolver) { - resolver = InstrumentedAddressResolverGroup.wrap(connectionInstrumenter(), resolver); + @Advice.AssignReturned.ToArguments(@ToArgument(0)) + public static AddressResolverGroup onEnter( + @Advice.Argument(value = 0) AddressResolverGroup resolver) { + return InstrumentedAddressResolverGroup.wrap(connectionInstrumenter(), resolver); } } @SuppressWarnings("unused") public static class ConnectAdvice { @Advice.OnMethodEnter - public static void startConnect( - @Advice.Argument(0) SocketAddress remoteAddress, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelRequest") NettyConnectionRequest request, - @Advice.Local("otelScope") Scope scope) { + public static NettyScope startConnect(@Advice.Argument(0) SocketAddress remoteAddress) { Context parentContext = Java8BytecodeBridge.currentContext(); - request = NettyConnectionRequest.connect(remoteAddress); + NettyConnectionRequest request = NettyConnectionRequest.connect(remoteAddress); if (!connectionInstrumenter().shouldStart(parentContext, request)) { - return; + return null; } - context = connectionInstrumenter().start(parentContext, request); - scope = context.makeCurrent(); + return NettyScope.startNew(connectionInstrumenter(), parentContext, request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endConnect( @Advice.Thrown Throwable throwable, @Advice.Argument(2) ChannelPromise channelPromise, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelRequest") NettyConnectionRequest request, - @Advice.Local("otelScope") Scope scope) { + @Advice.Enter NettyScope enterScope) { - if (scope == null) { - return; - } - scope.close(); - - if (throwable != null) { - connectionInstrumenter().end(context, request, null, throwable); - } else { - channelPromise.addListener( - new ConnectionCompleteListener(connectionInstrumenter(), context, request)); - } + NettyScope.end(enterScope, connectionInstrumenter(), channelPromise, throwable); } } } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java index 75e45800d591..9bdb10ef14c0 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java @@ -52,9 +52,7 @@ public void transform(TypeTransformer transformer) { public static class ChannelPipelineAddAdvice { @Advice.OnMethodEnter - public static void trackCallDepth( - @Advice.Argument(2) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + public static CallDepth trackCallDepth(@Advice.Argument(2) ChannelHandler handler) { // Previously we used one unique call depth tracker for all handlers, using // ChannelPipeline.class as a key. // The problem with this approach is that it does not work with netty's @@ -64,8 +62,9 @@ public static void trackCallDepth( // Using the specific handler key instead of the generic ChannelPipeline.class will help us // both to handle such cases and avoid adding our additional handlers in case of internal // calls of `addLast` to other method overloads with a compatible signature. - callDepth = CallDepth.forClass(handler.getClass()); + CallDepth callDepth = CallDepth.forClass(handler.getClass()); callDepth.getAndIncrement(); + return callDepth; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @@ -73,7 +72,8 @@ public static void addHandler( @Advice.This ChannelPipeline pipeline, @Advice.Argument(1) String handlerName, @Advice.Argument(2) ChannelHandler handler, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Enter CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { return; } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyInstrumentationModule.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyInstrumentationModule.java index 6f5f5a28038b..9d9c71505726 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyInstrumentationModule.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyInstrumentationModule.java @@ -11,12 +11,14 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyFutureInstrumentation; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class NettyInstrumentationModule extends InstrumentationModule { +public class NettyInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public NettyInstrumentationModule() { super("netty", "netty-4.1"); } @@ -29,10 +31,8 @@ public ElementMatcher.Junction classLoaderMatcher() { } @Override - public boolean isIndyModule() { - // netty instrumentation classes are used in other instrumentations which causes class cast - // exceptions - return false; + public String getModuleGroup() { + return "netty"; } @Override diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java index f18bd38bc89a..ea9947441b89 100644 --- a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java @@ -7,7 +7,6 @@ import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; -import java.util.Deque; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -17,9 +16,9 @@ public final class AttributeKeys { // this is the context that has the server span // - // note: this attribute key is also used by ratpack instrumentation - public static final AttributeKey> SERVER_CONTEXT = - AttributeKey.valueOf(AttributeKeys.class, "server-context"); + // note: this attribute key is also used by finagle instrumentation + public static final AttributeKey SERVER_CONTEXTS = + AttributeKey.valueOf(AttributeKeys.class, "server-contexts"); public static final AttributeKey CLIENT_CONTEXT = AttributeKey.valueOf(AttributeKeys.class, "client-context"); diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/ServerContexts.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/ServerContexts.java new file mode 100644 index 000000000000..34697a006200 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/ServerContexts.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1.internal; + +import io.netty.channel.Channel; +import io.netty.util.Attribute; +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * A helper class for keeping track of incoming requests and spans associated with them. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ServerContexts { + private static final int PIPELINING_LIMIT = 1000; + // With http pipelining multiple requests can be sent on the same connection. Responses should be + // sent in the same order the requests came in. We use this deque to store the request context + // and pop elements as responses are sent. + private final Deque serverContexts = new ArrayDeque<>(); + private volatile boolean broken = false; + + private ServerContexts() {} + + public static ServerContexts get(Channel channel) { + return channel.attr(AttributeKeys.SERVER_CONTEXTS).get(); + } + + public static ServerContexts getOrCreate(Channel channel) { + Attribute attribute = channel.attr(AttributeKeys.SERVER_CONTEXTS); + ServerContexts result = attribute.get(); + if (result == null) { + result = new ServerContexts(); + attribute.set(result); + } + return result; + } + + public static ServerContext peekFirst(Channel channel) { + ServerContexts serverContexts = get(channel); + return serverContexts != null ? serverContexts.peekFirst() : null; + } + + public ServerContext peekFirst() { + return serverContexts.peekFirst(); + } + + public ServerContext peekLast() { + return serverContexts.peekFirst(); + } + + public ServerContext pollFirst() { + return serverContexts.pollFirst(); + } + + public ServerContext pollLast() { + return serverContexts.pollLast(); + } + + public void addLast(ServerContext context) { + if (broken) { + return; + } + // If the pipelining limit is exceeded we'll stop tracing and mark the channel as broken. + // Exceeding the limit indicates that there is good chance that server context are not removed + // from the deque and there could be a memory leak. This could happen when http server decides + // not to send response to some requests, for example see + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/11942 + if (serverContexts.size() > PIPELINING_LIMIT) { + broken = true; + serverContexts.clear(); + } + serverContexts.addLast(context); + } +} diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java index 14618125bd8f..8f5b9a56fe84 100644 --- a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java @@ -10,16 +10,12 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; -import java.util.ArrayDeque; -import java.util.Deque; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -37,7 +33,7 @@ public HttpServerRequestTracingHandler( @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Channel channel = ctx.channel(); - Deque serverContexts = getOrCreate(channel, AttributeKeys.SERVER_CONTEXT); + ServerContexts serverContexts = ServerContexts.getOrCreate(channel); if (!(msg instanceof HttpRequest)) { ServerContext serverContext = serverContexts.peekLast(); @@ -66,8 +62,10 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception // the span is ended normally in HttpServerResponseTracingHandler } catch (Throwable throwable) { // make sure to remove the server context on end() call - ServerContext serverContext = serverContexts.removeLast(); - instrumenter.end(serverContext.context(), serverContext.request(), null, throwable); + ServerContext serverContext = serverContexts.pollLast(); + if (serverContext != null) { + instrumenter.end(serverContext.context(), serverContext.request(), null, throwable); + } throw throwable; } } @@ -75,8 +73,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // connection was closed, close all remaining requests - Attribute> contextAttr = ctx.channel().attr(AttributeKeys.SERVER_CONTEXT); - Deque serverContexts = contextAttr.get(); + ServerContexts serverContexts = ServerContexts.get(ctx.channel()); if (serverContexts == null) { super.channelInactive(ctx); @@ -89,14 +86,4 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception { } super.channelInactive(ctx); } - - private static Deque getOrCreate(Channel channel, AttributeKey> key) { - Attribute> attribute = channel.attr(key); - Deque deque = attribute.get(); - if (deque == null) { - deque = new ArrayDeque<>(); - attribute.set(deque); - } - return deque; - } } diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java index 4bdcf99a777e..2c89e310c6e5 100644 --- a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java @@ -13,18 +13,16 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.Attribute; import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.instrumentation.netty.v4_1.internal.ProtocolEventHandler; import io.opentelemetry.instrumentation.netty.v4_1.internal.ProtocolSpecificEvent; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; -import java.util.Deque; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import javax.annotation.Nullable; /** @@ -51,12 +49,8 @@ public HttpServerResponseTracingHandler( @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) throws Exception { - Attribute> serverContextAttr = - ctx.channel().attr(AttributeKeys.SERVER_CONTEXT); - - Deque serverContexts = serverContextAttr.get(); + ServerContexts serverContexts = ServerContexts.get(ctx.channel()); ServerContext serverContext = serverContexts != null ? serverContexts.peekFirst() : null; - if (serverContext == null) { super.write(ctx, msg, prm); return; @@ -86,7 +80,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) thr } else { // Headers and body all sent together, we have the response information in the msg. beforeCommitHandler.handle(serverContext.context(), (HttpResponse) msg); - serverContexts.removeFirst(); + serverContexts.pollFirst(); writePromise.addListener( future -> end( @@ -102,7 +96,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) thr // Body sent after headers. We stored the response information in the context when // encountering HttpResponse (which was not FullHttpResponse since it's not // LastHttpContent). - serverContexts.removeFirst(); + serverContexts.pollFirst(); HttpResponse response = ctx.channel().attr(HTTP_SERVER_RESPONSE).getAndSet(null); writePromise.addListener( future -> @@ -130,7 +124,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) thr try (Scope ignored = serverContext.context().makeCurrent()) { super.write(ctx, msg, writePromise); } catch (Throwable throwable) { - serverContexts.removeFirst(); + serverContexts.pollFirst(); end(serverContext.context(), serverContext.request(), null, throwable); throw throwable; } diff --git a/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java b/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java index 2cc4f6f28975..f7e982f7edab 100644 --- a/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java +++ b/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java @@ -15,13 +15,12 @@ import io.opentelemetry.instrumentation.okhttp.v3_0.internal.TracingInterceptor; import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters; import okhttp3.Interceptor; -import okhttp3.Request; import okhttp3.Response; /** Holder of singleton interceptors for adding to instrumented clients. */ public final class OkHttp3Singletons { - private static final Instrumenter INSTRUMENTER = + private static final Instrumenter INSTRUMENTER = JavaagentHttpClientInstrumenters.create( OkHttpClientInstrumenterBuilderFactory.create(GlobalOpenTelemetry.get())); diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java index 22e6b1cf9995..cd4963f24654 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java @@ -14,7 +14,6 @@ import okhttp3.Callback; import okhttp3.Interceptor; import okhttp3.OkHttpClient; -import okhttp3.Request; import okhttp3.Response; /** Entrypoint for instrumenting OkHttp clients. */ @@ -32,10 +31,11 @@ public static OkHttpTelemetryBuilder builder(OpenTelemetry openTelemetry) { return new OkHttpTelemetryBuilder(openTelemetry); } - private final Instrumenter instrumenter; + private final Instrumenter instrumenter; private final ContextPropagators propagators; - OkHttpTelemetry(Instrumenter instrumenter, ContextPropagators propagators) { + OkHttpTelemetry( + Instrumenter instrumenter, ContextPropagators propagators) { this.instrumenter = instrumenter; this.propagators = propagators; } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java index 552db08382e0..1623ad127d8b 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java @@ -15,13 +15,13 @@ import java.util.List; import java.util.Set; import java.util.function.Function; -import okhttp3.Request; +import okhttp3.Interceptor; import okhttp3.Response; /** A builder of {@link OkHttpTelemetry}. */ public final class OkHttpTelemetryBuilder { - private final DefaultHttpClientInstrumenterBuilder builder; + private final DefaultHttpClientInstrumenterBuilder builder; OkHttpTelemetryBuilder(OpenTelemetry openTelemetry) { builder = OkHttpClientInstrumenterBuilderFactory.create(openTelemetry); @@ -33,7 +33,7 @@ public final class OkHttpTelemetryBuilder { */ @CanIgnoreReturnValue public OkHttpTelemetryBuilder addAttributeExtractor( - AttributesExtractor attributesExtractor) { + AttributesExtractor attributesExtractor) { builder.addAttributeExtractor(attributesExtractor); return this; } @@ -95,7 +95,9 @@ public OkHttpTelemetryBuilder setEmitExperimentalHttpClientMetrics( /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public OkHttpTelemetryBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function< + SpanNameExtractor, + ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); return this; diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/ConnectionErrorSpanInterceptor.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/ConnectionErrorSpanInterceptor.java index e34a2e4b3d69..3da6df3b25ce 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/ConnectionErrorSpanInterceptor.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/ConnectionErrorSpanInterceptor.java @@ -21,9 +21,9 @@ */ public final class ConnectionErrorSpanInterceptor implements Interceptor { - private final Instrumenter instrumenter; + private final Instrumenter instrumenter; - public ConnectionErrorSpanInterceptor(Instrumenter instrumenter) { + public ConnectionErrorSpanInterceptor(Instrumenter instrumenter) { this.instrumenter = instrumenter; } @@ -43,9 +43,9 @@ public Response intercept(Chain chain) throws IOException { } finally { // only create a span when there wasn't any HTTP request if (HttpClientRequestResendCount.get(parentContext) == 0) { - if (instrumenter.shouldStart(parentContext, request)) { + if (instrumenter.shouldStart(parentContext, chain)) { InstrumenterUtil.startAndEnd( - instrumenter, parentContext, request, response, error, startTime, Instant.now()); + instrumenter, parentContext, chain, response, error, startTime, Instant.now()); } } } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpAttributesGetter.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpAttributesGetter.java index b6b9aab4a5ff..0043f7dd52ed 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpAttributesGetter.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpAttributesGetter.java @@ -6,47 +6,52 @@ package io.opentelemetry.instrumentation.okhttp.v3_0.internal; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.List; import javax.annotation.Nullable; -import okhttp3.Request; +import okhttp3.Connection; +import okhttp3.Interceptor; import okhttp3.Response; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public enum OkHttpAttributesGetter implements HttpClientAttributesGetter { +public enum OkHttpAttributesGetter + implements HttpClientAttributesGetter { INSTANCE; @Override - public String getHttpRequestMethod(Request request) { - return request.method(); + public String getHttpRequestMethod(Interceptor.Chain chain) { + return chain.request().method(); } @Override - public String getUrlFull(Request request) { - return request.url().toString(); + public String getUrlFull(Interceptor.Chain chain) { + return chain.request().url().toString(); } @Override - public List getHttpRequestHeader(Request request, String name) { - return request.headers(name); + public List getHttpRequestHeader(Interceptor.Chain chain, String name) { + return chain.request().headers(name); } @Override public Integer getHttpResponseStatusCode( - Request request, Response response, @Nullable Throwable error) { + Interceptor.Chain chain, Response response, @Nullable Throwable error) { return response.code(); } @Override - public List getHttpResponseHeader(Request request, Response response, String name) { + public List getHttpResponseHeader( + Interceptor.Chain chain, Response response, String name) { return response.headers(name); } @Nullable @Override - public String getNetworkProtocolName(Request request, @Nullable Response response) { + public String getNetworkProtocolName(Interceptor.Chain chain, @Nullable Response response) { if (response == null) { return null; } @@ -67,7 +72,7 @@ public String getNetworkProtocolName(Request request, @Nullable Response respons @Nullable @Override - public String getNetworkProtocolVersion(Request request, @Nullable Response response) { + public String getNetworkProtocolVersion(Interceptor.Chain chain, @Nullable Response response) { if (response == null) { return null; } @@ -90,12 +95,28 @@ public String getNetworkProtocolVersion(Request request, @Nullable Response resp @Override @Nullable - public String getServerAddress(Request request) { - return request.url().host(); + public String getServerAddress(Interceptor.Chain chain) { + return chain.request().url().host(); } @Override - public Integer getServerPort(Request request) { - return request.url().port(); + public Integer getServerPort(Interceptor.Chain chain) { + return chain.request().url().port(); + } + + @Nullable + @Override + public InetSocketAddress getNetworkPeerInetSocketAddress( + Interceptor.Chain chain, @Nullable Response response) { + Connection connection = chain.connection(); + if (connection == null) { + return null; + } + SocketAddress socketAddress = connection.socket().getRemoteSocketAddress(); + if (socketAddress instanceof InetSocketAddress) { + return (InetSocketAddress) socketAddress; + } else { + return null; + } } } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpClientInstrumenterBuilderFactory.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpClientInstrumenterBuilderFactory.java index 9ad002acda5e..4dbaf771d24d 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpClientInstrumenterBuilderFactory.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpClientInstrumenterBuilderFactory.java @@ -7,7 +7,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; -import okhttp3.Request; +import okhttp3.Interceptor; import okhttp3.Response; /** @@ -19,7 +19,7 @@ public class OkHttpClientInstrumenterBuilderFactory { private OkHttpClientInstrumenterBuilderFactory() {} - public static DefaultHttpClientInstrumenterBuilder create( + public static DefaultHttpClientInstrumenterBuilder create( OpenTelemetry openTelemetry) { return new DefaultHttpClientInstrumenterBuilder<>( INSTRUMENTATION_NAME, openTelemetry, OkHttpAttributesGetter.INSTANCE); diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/TracingInterceptor.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/TracingInterceptor.java index 202dfabf30e6..14f37fb687f8 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/TracingInterceptor.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/TracingInterceptor.java @@ -20,11 +20,11 @@ */ public final class TracingInterceptor implements Interceptor { - private final Instrumenter instrumenter; + private final Instrumenter instrumenter; private final ContextPropagators propagators; public TracingInterceptor( - Instrumenter instrumenter, ContextPropagators propagators) { + Instrumenter instrumenter, ContextPropagators propagators) { this.instrumenter = instrumenter; this.propagators = propagators; } @@ -34,11 +34,11 @@ public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Context parentContext = Context.current(); - if (!instrumenter.shouldStart(parentContext, request)) { + if (!instrumenter.shouldStart(parentContext, chain)) { return chain.proceed(chain.request()); } - Context context = instrumenter.start(parentContext, request); + Context context = instrumenter.start(parentContext, chain); request = injectContextToRequest(request, context); Response response = null; @@ -50,7 +50,7 @@ public Response intercept(Chain chain) throws IOException { error = e; throw e; } finally { - instrumenter.end(context, request, response, error); + instrumenter.end(context, chain, response, error); } } diff --git a/instrumentation/opencensus-shim/testing/src/test/java/io/opentelemetry/opencensusshim/JavaagentInstrumentationTest.java b/instrumentation/opencensus-shim/testing/src/test/java/io/opentelemetry/opencensusshim/JavaagentInstrumentationTest.java index 6731250b5685..02a11c5754d4 100644 --- a/instrumentation/opencensus-shim/testing/src/test/java/io/opentelemetry/opencensusshim/JavaagentInstrumentationTest.java +++ b/instrumentation/opencensus-shim/testing/src/test/java/io/opentelemetry/opencensusshim/JavaagentInstrumentationTest.java @@ -5,6 +5,8 @@ package io.opentelemetry.opencensusshim; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + import io.opencensus.trace.AttributeValue; import io.opencensus.trace.Tracing; import io.opencensus.trace.samplers.Samplers; @@ -15,7 +17,6 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractBooleanAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -84,9 +85,9 @@ void testInterleavedSpansOcFirst() { .hasNoParent() .hasAttribute(AttributeKey.booleanKey("outer"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("inner"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("middle"), AbstractBooleanAssert::isNull)), // middle span sa -> @@ -94,9 +95,9 @@ void testInterleavedSpansOcFirst() { .hasParent(ta.getSpan(0)) .hasAttribute(AttributeKey.booleanKey("middle"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("inner"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("outer"), AbstractBooleanAssert::isNull)), // inner span sa -> @@ -104,9 +105,9 @@ void testInterleavedSpansOcFirst() { .hasParent(ta.getSpan(1)) .hasAttribute(AttributeKey.booleanKey("inner"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("middle"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("outer"), AbstractBooleanAssert::isNull)))); } @@ -159,9 +160,9 @@ void testInterleavedSpansOtelFirst() { .hasNoParent() .hasAttribute(AttributeKey.booleanKey("outer"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("inner"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("middle"), AbstractBooleanAssert::isNull)), // middle span sa -> @@ -169,9 +170,9 @@ void testInterleavedSpansOtelFirst() { .hasParent(ta.getSpan(0)) .hasAttribute(AttributeKey.booleanKey("middle"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("inner"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("outer"), AbstractBooleanAssert::isNull)), // inner span sa -> @@ -179,9 +180,9 @@ void testInterleavedSpansOtelFirst() { .hasParent(ta.getSpan(1)) .hasAttribute(AttributeKey.booleanKey("inner"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("middle"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("outer"), AbstractBooleanAssert::isNull)))); } @@ -299,9 +300,9 @@ void testNestedOpenCensusSpans() { .hasNoParent() .hasAttribute(AttributeKey.booleanKey("outer"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("inner"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("middle"), AbstractBooleanAssert::isNull)), // middle span sa -> @@ -309,9 +310,9 @@ void testNestedOpenCensusSpans() { .hasParent(ta.getSpan(0)) .hasAttribute(AttributeKey.booleanKey("middle"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("inner"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("outer"), AbstractBooleanAssert::isNull)), // inner span sa -> @@ -319,9 +320,9 @@ void testNestedOpenCensusSpans() { .hasParent(ta.getSpan(1)) .hasAttribute(AttributeKey.booleanKey("inner"), true) .hasAttributesSatisfying( - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("middle"), AbstractBooleanAssert::isNull), - OpenTelemetryAssertions.satisfies( + satisfies( AttributeKey.booleanKey("outer"), AbstractBooleanAssert::isNull)))); } } diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy deleted file mode 100644 index 3b3778b25374..000000000000 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.api.baggage.Baggage -import io.opentelemetry.api.trace.Span -import io.opentelemetry.context.Context -import io.opentelemetry.context.ContextKey -import io.opentelemetry.instrumentation.annotations.WithSpan -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes - -import java.util.concurrent.CountDownLatch -import java.util.concurrent.Executors -import java.util.concurrent.atomic.AtomicReference - -class ContextBridgeTest extends AgentInstrumentationSpecification { - - private static final ContextKey ANIMAL = ContextKey.named("animal") - - def "agent propagates application's context"() { - when: - def context = Context.current().with(ANIMAL, "cat") - def captured = new AtomicReference() - context.makeCurrent().withCloseable { - Executors.newSingleThreadExecutor().submit({ - captured.set(Context.current().get(ANIMAL)) - }).get() - } - - then: - captured.get() == "cat" - } - - def "application propagates agent's context"() { - given: - def runnable = new Runnable() { - @WithSpan("test") - @Override - void run() { - // using @WithSpan above to make the agent generate a context - // and then using manual propagation below to verify that context can be propagated by user - def context = Context.current() - Context.root().makeCurrent().withCloseable { - Span.current().setAttribute("dog", "no") - context.makeCurrent().withCloseable { - Span.current().setAttribute("cat", "yes") - } - } - } - } - - when: - runnable.run() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - hasNoParent() - attributes { - "$CodeIncubatingAttributes.CODE_NAMESPACE" runnable.class.name - "$CodeIncubatingAttributes.CODE_FUNCTION" "run" - "cat" "yes" - } - } - } - } - } - - def "agent propagates application's span"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.makeCurrent().withCloseable { - Executors.newSingleThreadExecutor().submit({ - Span.current().setAttribute("cat", "yes") - }).get() - } - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - hasNoParent() - attributes { - "cat" "yes" - } - } - } - } - } - - def "application propagates agent's span"() { - given: - def runnable = new Runnable() { - @WithSpan("test") - @Override - void run() { - // using @WithSpan above to make the agent generate a span - // and then using manual propagation below to verify that span can be propagated by user - def span = Span.current() - Context.root().makeCurrent().withCloseable { - Span.current().setAttribute("dog", "no") - span.makeCurrent().withCloseable { - Span.current().setAttribute("cat", "yes") - } - } - } - } - - when: - runnable.run() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - hasNoParent() - attributes { - "$CodeIncubatingAttributes.CODE_NAMESPACE" runnable.class.name - "$CodeIncubatingAttributes.CODE_FUNCTION" "run" - "cat" "yes" - } - } - } - } - } - - def "agent propagates application's baggage"() { - when: - def testBaggage = Baggage.builder().put("cat", "yes").build() - def ref = new AtomicReference() - def latch = new CountDownLatch(1) - testBaggage.makeCurrent().withCloseable { - Executors.newSingleThreadExecutor().submit({ - ref.set(Baggage.current()) - latch.countDown() - }).get() - } - - then: - latch.await() - ref.get().size() == 1 - ref.get().getEntryValue("cat") == "yes" - } - - def "test empty current context is root context"() { - expect: - Context.current() == Context.root() - } - - // TODO (trask) - // more tests are needed here, not sure how to implement, probably need to write some test - // instrumentation to help test, similar to :testing-common:integration-tests - // - // * "application propagates agent's baggage" - // * "agent uses application's span" - // * "application uses agent's span" (this is covered above by "application propagates agent's span") - // * "agent uses application's baggage" - // * "application uses agent's baggage" -} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextTest.groovy b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextTest.groovy deleted file mode 100644 index ba2d52a68708..000000000000 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextTest.groovy +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.api.trace.Span -import io.opentelemetry.context.Context -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification - -class ContextTest extends AgentInstrumentationSpecification { - - def "Span.current() should return invalid"() { - when: - def span = Span.current() - - then: - !span.spanContext.valid - } - - def "Span.current() should return span"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - def scope = testSpan.makeCurrent() - def span = Span.current() - scope.close() - - then: - span == testSpan - } - - def "Span.fromContext should return invalid"() { - when: - def span = Span.fromContext(Context.current()) - - then: - !span.spanContext.valid - } - - def "getSpan should return span"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - def scope = testSpan.makeCurrent() - def span = Span.fromContext(Context.current()) - scope.close() - - then: - span == testSpan - } - - def "Span.fromContextOrNull should return null"() { - when: - def span = Span.fromContextOrNull(Context.current()) - - then: - span == null - } - - def "Span.fromContextOrNull should return span"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - def scope = testSpan.makeCurrent() - def span = Span.fromContextOrNull(Context.current()) - scope.close() - - then: - span == testSpan - } -} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/TracerTest.groovy b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/TracerTest.groovy deleted file mode 100644 index be63f3d40255..000000000000 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/TracerTest.groovy +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.api.common.Attributes -import io.opentelemetry.api.trace.Span -import io.opentelemetry.context.Context -import io.opentelemetry.context.Scope -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.ExceptionAttributes - -import static io.opentelemetry.api.trace.SpanKind.PRODUCER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -class TracerTest extends AgentInstrumentationSpecification { - - def "capture span, kind, attributes, and status"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan() - testSpan.setAttribute("string", "1") - testSpan.setAttribute("long", 2) - testSpan.setAttribute("double", 3.0) - testSpan.setAttribute("boolean", true) - testSpan.setStatus(ERROR) - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - kind PRODUCER - hasNoParent() - status ERROR - attributes { - "string" "1" - "long" 2 - "double" 3.0 - "boolean" true - } - } - } - } - } - - def "capture span with implicit parent using Tracer.withSpan()"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - Span parentSpan = tracer.spanBuilder("parent").startSpan() - Scope parentScope = Context.current().with(parentSpan).makeCurrent() - - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.end() - - parentSpan.end() - parentScope.close() - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "parent" - hasNoParent() - attributes { - } - } - span(1) { - name "test" - childOf span(0) - attributes { - } - } - } - } - } - - def "capture span with implicit parent using makeCurrent"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - Span parentSpan = tracer.spanBuilder("parent").startSpan() - Scope parentScope = parentSpan.makeCurrent() - - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.end() - - parentSpan.end() - parentScope.close() - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "parent" - hasNoParent() - attributes { - } - } - span(1) { - name "test" - childOf span(0) - attributes { - } - } - } - } - } - - def "capture span with implicit parent using TracingContextUtils.withSpan and makeCurrent"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - Span parentSpan = tracer.spanBuilder("parent").startSpan() - def parentContext = Context.current().with(parentSpan) - Scope parentScope = parentContext.makeCurrent() - - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.end() - - parentSpan.end() - parentScope.close() - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "parent" - hasNoParent() - attributes { - } - } - span(1) { - name "test" - childOf span(0) - attributes { - } - } - } - } - } - - def "capture span with explicit parent"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def parentSpan = tracer.spanBuilder("parent").startSpan() - def context = Context.root().with(parentSpan) - def testSpan = tracer.spanBuilder("test").setParent(context).startSpan() - testSpan.end() - parentSpan.end() - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "parent" - hasNoParent() - attributes { - } - } - span(1) { - name "test" - childOf span(0) - attributes { - } - } - } - } - } - - def "capture span with explicit no parent"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def parentSpan = tracer.spanBuilder("parent").startSpan() - def parentScope = parentSpan.makeCurrent() - def testSpan = tracer.spanBuilder("test").setNoParent().startSpan() - testSpan.end() - parentSpan.end() - parentScope.close() - - then: - assertTraces(2) { - traces.sort(orderByRootSpanName("parent", "test")) - trace(0, 1) { - span(0) { - name "parent" - hasNoParent() - attributes { - } - } - } - trace(1, 1) { - span(0) { - name "test" - hasNoParent() - attributes { - } - } - } - } - } - - def "capture name update"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.updateName("test2") - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test2" - hasNoParent() - attributes { - } - } - } - } - } - - def "capture exception()"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.recordException(new IllegalStateException()) - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - event(0) { - eventName("exception") - attributes { - "$ExceptionAttributes.EXCEPTION_TYPE" "java.lang.IllegalStateException" - "$ExceptionAttributes.EXCEPTION_STACKTRACE" String - } - } - attributes { - } - } - } - } - } - - def "capture exception with Attributes()"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - testSpan.recordException( - new IllegalStateException(), - Attributes.builder().put("dog", "bark").build()) - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - event(0) { - eventName("exception") - attributes { - "$ExceptionAttributes.EXCEPTION_TYPE" "java.lang.IllegalStateException" - "$ExceptionAttributes.EXCEPTION_STACKTRACE" String - "dog" "bark" - } - } - attributes { - } - } - } - } - } - - def "capture name update using TracingContextUtils.getCurrentSpan()"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - def testScope = Context.current().with(testSpan).makeCurrent() - Span.current().updateName("test2") - testScope.close() - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test2" - hasNoParent() - attributes { - } - } - } - } - } - - def "capture name update using TracingContextUtils.Span.fromContext(Context.current())"() { - when: - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").startSpan() - def testScope = Context.current().with(testSpan).makeCurrent() - Span.fromContext(Context.current()).updateName("test2") - testScope.close() - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test2" - hasNoParent() - attributes { - } - } - } - } - } - - def "add wrapped span to context"() { - when: - // Lazy way to get a span context - def tracer = GlobalOpenTelemetry.getTracer("test") - def testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan() - testSpan.end() - - def span = Span.wrap(testSpan.getSpanContext()) - def context = Context.current().with(span) - - then: - Span.fromContext(context).getSpanContext().getSpanId() == span.getSpanContext().getSpanId() - } - - // this test uses opentelemetry-api-1.4 instrumentation - def "test tracer builder"() { - when: - def tracer = GlobalOpenTelemetry.get().tracerBuilder("test").setInstrumentationVersion("1.2.3").build() - def testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan() - testSpan.end() - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "test" - kind PRODUCER - hasNoParent() - instrumentationLibraryVersion "1.2.3" - attributes { - } - } - } - } - } -} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/ContextBridgeTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/ContextBridgeTest.java new file mode 100644 index 000000000000..d73dc4b80fa6 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/ContextBridgeTest.java @@ -0,0 +1,197 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class ContextBridgeTest { + + @RegisterExtension + private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private static final ContextKey ANIMAL = ContextKey.named("animal"); + + @Test + @DisplayName("agent propagates application's context") + void agentPropagatesApplicationsContext() throws Exception { + // When + Context context = Context.current().with(ANIMAL, "cat"); + AtomicReference captured = new AtomicReference<>(); + try (Scope ignored = context.makeCurrent()) { + Executors.newSingleThreadExecutor() + .submit(() -> captured.set(Context.current().get(ANIMAL))) + .get(); + } + + // Then + assertThat(captured.get()).isEqualTo("cat"); + } + + @Test + @DisplayName("application propagates agent's context") + void applicationPropagatesAgentsContext() { + // Given + Runnable runnable = + new Runnable() { + @WithSpan("test") + @Override + public void run() { + // using @WithSpan above to make the agent generate a context + // and then using manual propagation below to verify that context can be propagated by + // user + Context context = Context.current(); + try (Scope ignored = Context.root().makeCurrent()) { + Span.current().setAttribute("dog", "no"); + try (Scope ignored2 = context.makeCurrent()) { + Span.current().setAttribute("cat", "yes"); + } + } + } + }; + + // When + runnable.run(); + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("test") + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo( + CodeIncubatingAttributes.CODE_NAMESPACE, + runnable.getClass().getName()), + equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "run"), + equalTo(stringKey("cat"), "yes")))); + } + + @Test + @DisplayName("agent propagates application's span") + void agentPropagatesApplicationsSpan() throws Exception { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + + Span testSpan = tracer.spanBuilder("test").startSpan(); + try (Scope ignored = testSpan.makeCurrent()) { + Executors.newSingleThreadExecutor() + .submit( + () -> { + Span.current().setAttribute("cat", "yes"); + }) + .get(); + } + testSpan.end(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("test") + .hasNoParent() + .hasAttributesSatisfyingExactly(equalTo(stringKey("cat"), "yes")))); + } + + @Test + @DisplayName("application propagates agent's span") + void applicationPropagatesAgentsSpan() { + // Given + Runnable runnable = + new Runnable() { + @WithSpan("test") + @Override + public void run() { + // using @WithSpan above to make the agent generate a span + // and then using manual propagation below to verify that span can be propagated by user + Span span = Span.current(); + try (Scope ignored = Context.root().makeCurrent()) { + Span.current().setAttribute("dog", "no"); + try (Scope ignored2 = span.makeCurrent()) { + Span.current().setAttribute("cat", "yes"); + } + } + } + }; + + // When + runnable.run(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("test") + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo( + CodeIncubatingAttributes.CODE_NAMESPACE, + runnable.getClass().getName()), + equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "run"), + equalTo(stringKey("cat"), "yes")))); + } + + @Test + @DisplayName("agent propagates application's baggage") + void agentPropagatesApplicationsBaggage() throws Exception { + // When + Baggage testBaggage = Baggage.builder().put("cat", "yes").build(); + AtomicReference ref = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + try (Scope ignored = testBaggage.makeCurrent()) { + Executors.newSingleThreadExecutor() + .submit( + () -> { + ref.set(Baggage.current()); + latch.countDown(); + }) + .get(); + } + + // Then + latch.await(); + assertThat(ref.get().size()).isEqualTo(1); + assertThat(ref.get().getEntryValue("cat")).isEqualTo("yes"); + } + + @Test + @DisplayName("test empty current context is root context") + void testEmptyCurrentContextIsRootContext() { + // Expect + assertThat(Context.current()).isEqualTo(Context.root()); + } + + // TODO (trask) + // more tests are needed here, not sure how to implement, probably need to write some test + // instrumentation to help test, similar to :testing-common:integration-tests + // + // * "application propagates agent's baggage" + // * "agent uses application's span" + // * "application uses agent's span" (this is covered above by "application propagates agent's + // span") + // * "agent uses application's baggage" + // * "application uses agent's baggage" +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/ContextTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/ContextTest.java new file mode 100644 index 000000000000..6f90fd3bffea --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/ContextTest.java @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ContextTest { + + @Test + @DisplayName("Span.current() should return invalid") + void spanCurrentShouldReturnInvalid() { + // When + Span span = Span.current(); + // Then + assertThat(span.getSpanContext().isValid()).isFalse(); + } + + @Test + @DisplayName("Span.current() should return span") + void spanCurrentShouldReturnSpan() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + try (Scope ignored = testSpan.makeCurrent()) { + Span span = Span.current(); + + // Then + assertThat(span).isEqualTo(testSpan); + } + } + + @Test + @DisplayName("Span.fromContext should return invalid") + void spanFromContextShouldReturnInvalid() { + // When + Span span = Span.fromContext(Context.current()); + + // Then + assertThat(span.getSpanContext().isValid()).isFalse(); + } + + @Test + @DisplayName("getSpan should return span") + void getSpanShouldReturnSpan() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + try (Scope ignored = testSpan.makeCurrent()) { + Span span = Span.fromContext(Context.current()); + + // Then + assertThat(span).isEqualTo(testSpan); + } + } + + @Test + @DisplayName("Span.fromContextOrNull should return null") + void spanFromContextOrNullShouldReturnNull() { + // When + Span span = Span.fromContextOrNull(Context.current()); + + // Then + assertThat(span).isNull(); + } + + @Test + @DisplayName("Span.fromContextOrNull should return span") + void spanFromContextOrNullShouldReturnSpan() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + try (Scope ignored = testSpan.makeCurrent()) { + Span span = Span.fromContextOrNull(Context.current()); + + // Then + assertThat(span).isEqualTo(testSpan); + } + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TracerTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TracerTest.java new file mode 100644 index 000000000000..404d47131fa6 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TracerTest.java @@ -0,0 +1,328 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.api.common.AttributeKey.doubleKey; +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.api.trace.SpanKind.PRODUCER; +import static io.opentelemetry.api.trace.StatusCode.ERROR; +import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.ExceptionAttributes; +import java.io.PrintWriter; +import java.io.StringWriter; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TracerTest { + + @RegisterExtension + private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Test + @DisplayName("capture span, kind, attributes, and status") + void captureSpanKindAttributesAndStatus() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan(); + testSpan.setAttribute("string", "1"); + testSpan.setAttribute("long", 2L); + testSpan.setAttribute("double", 3.0); + testSpan.setAttribute("boolean", true); + testSpan.setStatus(ERROR); + testSpan.end(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("test") + .hasKind(PRODUCER) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasAttributesSatisfyingExactly( + equalTo(stringKey("string"), "1"), + equalTo(longKey("long"), 2L), + equalTo(doubleKey("double"), 3.0), + equalTo(booleanKey("boolean"), true)))); + } + + @Test + @DisplayName("capture span with implicit parent using Tracer.withSpan()") + void captureSpanWithImplicitParentUsingTracerWithSpan() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span parentSpan = tracer.spanBuilder("parent").startSpan(); + Scope parentScope = Context.current().with(parentSpan).makeCurrent(); + + Span testSpan = tracer.spanBuilder("test").startSpan(); + testSpan.end(); + + parentSpan.end(); + parentScope.close(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasTotalAttributeCount(0), + span -> + span.hasName("test").hasParent(trace.getSpan(0)).hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("capture span with implicit parent using makeCurrent") + void captureSpanWithImplicitParentUsingMakeCurrent() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span parentSpan = tracer.spanBuilder("parent").startSpan(); + Scope parentScope = parentSpan.makeCurrent(); + + Span testSpan = tracer.spanBuilder("test").startSpan(); + testSpan.end(); + + parentSpan.end(); + parentScope.close(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasTotalAttributeCount(0), + span -> + span.hasName("test").hasParent(trace.getSpan(0)).hasTotalAttributeCount(0))); + } + + @Test + @DisplayName( + "capture span with implicit parent using TracingContextUtils.withSpan and makeCurrent") + void captureSpanWithImplicitParentUsingTracingContextUtilsWithSpanAndMakeCurrent() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span parentSpan = tracer.spanBuilder("parent").startSpan(); + Context parentContext = Context.current().with(parentSpan); + Scope parentScope = parentContext.makeCurrent(); + + Span testSpan = tracer.spanBuilder("test").startSpan(); + testSpan.end(); + + parentSpan.end(); + parentScope.close(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasTotalAttributeCount(0), + span -> + span.hasName("test").hasParent(trace.getSpan(0)).hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("capture span with explicit parent") + void captureSpanWithExplicitParent() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span parentSpan = tracer.spanBuilder("parent").startSpan(); + Context context = Context.root().with(parentSpan); + Span testSpan = tracer.spanBuilder("test").setParent(context).startSpan(); + testSpan.end(); + parentSpan.end(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasTotalAttributeCount(0), + span -> + span.hasName("test").hasParent(trace.getSpan(0)).hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("capture span with explicit no parent") + void captureSpanWithExplicitNoParent() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span parentSpan = tracer.spanBuilder("parent").startSpan(); + Scope parentScope = parentSpan.makeCurrent(); + Span testSpan = tracer.spanBuilder("test").setNoParent().startSpan(); + testSpan.end(); + parentSpan.end(); + parentScope.close(); + + // Then + testing.waitAndAssertSortedTraces( + orderByRootSpanName("parent", "test"), + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasTotalAttributeCount(0)), + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("test").hasNoParent().hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("capture name update") + void captureNameUpdate() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + testSpan.updateName("test2"); + testSpan.end(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("test2").hasNoParent().hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("capture exception") + void captureException() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + IllegalStateException throwable = new IllegalStateException(); + testSpan.recordException(throwable); + testSpan.end(); + + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("test").hasTotalAttributeCount(0).hasException(throwable))); + } + + @Test + @DisplayName("capture exception with Attributes") + void captureExceptionWithAttributes() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + IllegalStateException throwable = new IllegalStateException(); + testSpan.recordException(throwable, Attributes.builder().put("dog", "bark").build()); + testSpan.end(); + + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("test") + .hasTotalAttributeCount(0) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + equalTo( + ExceptionAttributes.EXCEPTION_TYPE, + "java.lang.IllegalStateException"), + equalTo( + ExceptionAttributes.EXCEPTION_STACKTRACE, + writer.toString()), + equalTo(stringKey("dog"), "bark"))))); + } + + @Test + @DisplayName("capture name update using TracingContextUtils.getCurrentSpan()") + void captureNameUpdateUsingTracingContextUtilsGetCurrentSpan() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + Scope testScope = Context.current().with(testSpan).makeCurrent(); + Span.current().updateName("test2"); + testScope.close(); + testSpan.end(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("test2").hasNoParent().hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("capture name update using TracingContextUtils.Span.fromContext(Context.current())") + void captureNameUpdateUsingTracingContextUtilsSpanFromContextCurrent() { + // When + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").startSpan(); + Scope testScope = Context.current().with(testSpan).makeCurrent(); + Span.fromContext(Context.current()).updateName("test2"); + testScope.close(); + testSpan.end(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("test2").hasNoParent().hasTotalAttributeCount(0))); + } + + @Test + @DisplayName("add wrapped span to context") + void addWrappedSpanToContext() { + // When + // Lazy way to get a span context + Tracer tracer = GlobalOpenTelemetry.getTracer("test"); + Span testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan(); + testSpan.end(); + + Span span = Span.wrap(testSpan.getSpanContext()); + Context context = Context.current().with(span); + + // Then + assertThat(Span.fromContext(context).getSpanContext().getSpanId()) + .isEqualTo(span.getSpanContext().getSpanId()); + } + + // this test uses opentelemetry-api-1.4 instrumentation + @Test + @DisplayName("test tracer builder") + void testTracerBuilder() { + // When + Tracer tracer = + GlobalOpenTelemetry.get().tracerBuilder("test").setInstrumentationVersion("1.2.3").build(); + Span testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan(); + testSpan.end(); + + // Then + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("test") + .hasKind(PRODUCER) + .hasNoParent() + .hasTotalAttributeCount(0) + .hasInstrumentationScopeInfo( + InstrumentationScopeInfo.builder("test").setVersion("1.2.3").build()))); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.31/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_31/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.31/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_31/metrics/MeterTest.java index be7da451a6a4..6c954a646d8f 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.31/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_31/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.31/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_31/metrics/MeterTest.java @@ -6,9 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_31.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleCounter; @@ -40,7 +40,6 @@ import io.opentelemetry.extension.incubator.metrics.LongGauge; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,7 +85,7 @@ void longCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -123,7 +122,7 @@ void doubleCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -160,7 +159,7 @@ void longUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -198,7 +197,7 @@ void doubleUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -236,7 +235,7 @@ void longHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -274,7 +273,7 @@ void doubleHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -311,7 +310,7 @@ void longGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -355,7 +354,7 @@ void syncLongGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -398,7 +397,7 @@ void doubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -441,7 +440,7 @@ void syncDoubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/incubator/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/incubator/metrics/MeterTest.java index 2c8446897eb2..4bd4470e2797 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/incubator/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/incubator/metrics/MeterTest.java @@ -6,9 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_32.incubator.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleCounter; @@ -40,7 +40,6 @@ import io.opentelemetry.extension.incubator.metrics.LongGauge; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,7 +85,7 @@ void longCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -123,7 +122,7 @@ void doubleCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -160,7 +159,7 @@ void longUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -198,7 +197,7 @@ void doubleUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -236,7 +235,7 @@ void longHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -274,7 +273,7 @@ void doubleHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -311,7 +310,7 @@ void longGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -355,7 +354,7 @@ void syncLongGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -398,7 +397,7 @@ void doubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -441,7 +440,7 @@ void syncDoubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/metrics/MeterTest.java index 982e47d4e651..aae3a68e7418 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/metrics/MeterTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_32.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; @@ -17,7 +18,6 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; @@ -61,7 +61,7 @@ void longHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -96,7 +96,7 @@ void doubleHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java index 84cc8c400360..ca78917ba4a9 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java @@ -6,9 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_37.incubator.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.metrics.DoubleGauge; @@ -40,7 +40,6 @@ import io.opentelemetry.api.metrics.ObservableLongGauge; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,7 +85,7 @@ void longCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -123,7 +122,7 @@ void doubleCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -160,7 +159,7 @@ void longUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -198,7 +197,7 @@ void doubleUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -236,7 +235,7 @@ void longHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -274,7 +273,7 @@ void doubleHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -311,7 +310,7 @@ void longGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -355,7 +354,7 @@ void syncLongGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -398,7 +397,7 @@ void doubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -441,7 +440,7 @@ void syncDoubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/oldAndNewIncubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/oldAndNewIncubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java index 84cc8c400360..ca78917ba4a9 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/oldAndNewIncubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/oldAndNewIncubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/metrics/MeterTest.java @@ -6,9 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_37.incubator.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.metrics.DoubleGauge; @@ -40,7 +40,6 @@ import io.opentelemetry.api.metrics.ObservableLongGauge; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,7 +85,7 @@ void longCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -123,7 +122,7 @@ void doubleCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -160,7 +159,7 @@ void longUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -198,7 +197,7 @@ void doubleUpDownCounter() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -236,7 +235,7 @@ void longHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -274,7 +273,7 @@ void doubleHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -311,7 +310,7 @@ void longGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -355,7 +354,7 @@ void syncLongGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -398,7 +397,7 @@ void doubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -441,7 +440,7 @@ void syncDoubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/metrics/MeterTest.java index 519656f2b27b..f5d2ecaf23fc 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/metrics/MeterTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_37.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; @@ -17,7 +18,6 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; @@ -61,7 +61,7 @@ void longHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -96,7 +96,7 @@ void doubleHistogram() { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/incubator/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/incubator/metrics/MeterTest.java index 986c17a04580..f8b9b1fbf94b 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/incubator/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/incubatorTest/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/incubator/metrics/MeterTest.java @@ -6,9 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_38.incubator.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGaugeBuilder; @@ -20,7 +20,6 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -66,7 +65,7 @@ void syncLongGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -108,7 +107,7 @@ void syncDoubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/metrics/MeterTest.java index adb929d4c75f..b2719df723e6 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/metrics/MeterTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_38.metrics; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import io.opentelemetry.api.common.Attributes; @@ -16,7 +17,6 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -59,7 +59,7 @@ void syncLongGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( @@ -98,7 +98,7 @@ void syncDoubleGauge() throws InterruptedException { metrics -> metrics.anySatisfy( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( diff --git a/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts b/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts index 2a887cc34d88..136a8c53bf76 100644 --- a/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts +++ b/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts @@ -13,10 +13,15 @@ muzzle { dependencies { library("com.oracle.database.jdbc:ucp:11.2.0.4") + library("com.oracle.database.jdbc:ojdbc8:12.2.0.1") implementation(project(":instrumentation:oracle-ucp-11.2:library")) testImplementation(project(":instrumentation:oracle-ucp-11.2:testing")) +} - latestDepTestLibrary("com.oracle.database.jdbc:ucp:21.9.0.0") +tasks { + test { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + } } diff --git a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/v11_2/UniversalConnectionPoolInstrumentation.java b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/v11_2/UniversalConnectionPoolInstrumentation.java index 2f0bd50f4b11..e048a7badc41 100644 --- a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/v11_2/UniversalConnectionPoolInstrumentation.java +++ b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/v11_2/UniversalConnectionPoolInstrumentation.java @@ -8,10 +8,11 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; import static io.opentelemetry.javaagent.instrumentation.oracleucp.v11_2.OracleUcpSingletons.telemetry; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import net.bytebuddy.asm.Advice; @@ -34,9 +35,7 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( - named("start") - .and(takesArguments(0).or(takesArguments(1).and(takesArgument(0, boolean.class)))), - this.getClass().getName() + "$StartAdvice"); + named("start").and(isPublic()), this.getClass().getName() + "$StartAdvice"); transformer.applyAdviceToMethod( named("stop").and(takesArguments(0)), this.getClass().getName() + "$StopAdvice"); } @@ -53,8 +52,20 @@ public static void onExit(@Advice.This UniversalConnectionPool connectionPool) { @SuppressWarnings("unused") public static class StopAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Local("otelCallDepth") CallDepth callDepth) { + callDepth = CallDepth.forClass(UniversalConnectionPool.class); + callDepth.getAndIncrement(); + } + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - public static void onExit(@Advice.This UniversalConnectionPool connectionPool) { + public static void onExit( + @Advice.This UniversalConnectionPool connectionPool, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + if (callDepth == null || callDepth.decrementAndGet() > 0) { + return; + } + telemetry().unregisterMetrics(connectionPool); } } diff --git a/instrumentation/oracle-ucp-11.2/library/build.gradle.kts b/instrumentation/oracle-ucp-11.2/library/build.gradle.kts index a2fa7819af1c..937adaaf315b 100644 --- a/instrumentation/oracle-ucp-11.2/library/build.gradle.kts +++ b/instrumentation/oracle-ucp-11.2/library/build.gradle.kts @@ -5,8 +5,13 @@ plugins { dependencies { library("com.oracle.database.jdbc:ucp:11.2.0.4") + library("com.oracle.database.jdbc:ojdbc8:12.2.0.1") testImplementation(project(":instrumentation:oracle-ucp-11.2:testing")) +} - latestDepTestLibrary("com.oracle.database.jdbc:ucp:21.9.0.0") +tasks { + test { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + } } diff --git a/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts b/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts index 2326f1bd8220..af0b5a74a3c6 100644 --- a/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts +++ b/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts @@ -4,8 +4,7 @@ plugins { dependencies { api(project(":testing-common")) - api("org.mockito:mockito-core") - api("org.mockito:mockito-junit-jupiter") + implementation("org.testcontainers:oracle-free") compileOnly("com.oracle.database.jdbc:ucp:11.2.0.4") } diff --git a/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java b/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java index 639240214874..548c8eb17ecd 100644 --- a/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java +++ b/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java @@ -9,9 +9,8 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.db.DbConnectionPoolMetricsAssertions; -import io.opentelemetry.instrumentation.testing.junit.db.MockDriver; import java.sql.Connection; -import java.sql.SQLException; +import java.time.Duration; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -19,15 +18,22 @@ import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.oracle.OracleContainer; -@ExtendWith(MockitoExtension.class) public abstract class AbstractOracleUcpInstrumentationTest { + private static final Logger logger = + LoggerFactory.getLogger(AbstractOracleUcpInstrumentationTest.class); + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.orcale-ucp-11.2"; + private static OracleContainer oracle; protected abstract InstrumentationExtension testing(); @@ -36,17 +42,42 @@ public abstract class AbstractOracleUcpInstrumentationTest { protected abstract void shutdown(PoolDataSource connectionPool) throws Exception; @BeforeAll - static void setUpMocks() throws SQLException { - MockDriver.register(); + static void setUp() { + // This docker image does not work on arm mac. To run this test on arm mac read + // https://blog.jdriven.com/2022/07/running-oracle-xe-with-testcontainers-on-apple-silicon/ + // install colima with brew install colima + // colima start --arch x86_64 --memory 4 + // export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock + // export DOCKER_HOST="unix://${HOME}/.colima/docker.sock" + String dockerHost = System.getenv("DOCKER_HOST"); + if (!"aarch64".equals(System.getProperty("os.arch")) + || (dockerHost != null && dockerHost.contains("colima"))) { + oracle = + new OracleContainer("gvenzl/oracle-free:23.4-slim-faststart") + .withLogConsumer(new Slf4jLogConsumer(logger)) + .withStartupTimeout(Duration.ofMinutes(2)); + oracle.start(); + } + } + + @AfterAll + static void cleanUp() { + if (oracle != null) { + oracle.stop(); + } } @ParameterizedTest @ValueSource(booleans = {true, false}) void shouldReportMetrics(boolean setExplicitPoolName) throws Exception { + Assumptions.assumeTrue(oracle != null); + // given PoolDataSource connectionPool = PoolDataSourceFactory.getPoolDataSource(); - connectionPool.setConnectionFactoryClassName(MockDriver.class.getName()); - connectionPool.setURL("jdbc:mock:testDatabase"); + connectionPool.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + connectionPool.setURL(oracle.getJdbcUrl()); + connectionPool.setUser(oracle.getUsername()); + connectionPool.setPassword(oracle.getPassword()); if (setExplicitPoolName) { connectionPool.setConnectionPoolName("testPool"); } diff --git a/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/ProcessMetricsTest.java b/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/ProcessMetricsTest.java index b1134922928a..f2b3dc14bdb0 100644 --- a/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/ProcessMetricsTest.java +++ b/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/ProcessMetricsTest.java @@ -5,11 +5,12 @@ package io.opentelemetry.instrumentation.oshi; +import static org.assertj.core.api.Assertions.assertThat; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -48,6 +49,6 @@ protected InstrumentationExtension testing() { @Test void verifyObservablesAreNotEmpty() { - Assertions.assertThat(observables).as("List of observables").isNotEmpty(); + assertThat(observables).as("List of observables").isNotEmpty(); } } diff --git a/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/SystemMetricsTest.java b/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/SystemMetricsTest.java index 4f2fd97fa623..958378004852 100644 --- a/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/SystemMetricsTest.java +++ b/instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/SystemMetricsTest.java @@ -5,11 +5,12 @@ package io.opentelemetry.instrumentation.oshi; +import static org.assertj.core.api.Assertions.assertThat; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -48,6 +49,6 @@ protected InstrumentationExtension testing() { @Test void verifyObservablesAreNotEmpty() { - Assertions.assertThat(observables).as("List of observables").isNotEmpty(); + assertThat(observables).as("List of observables").isNotEmpty(); } } diff --git a/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/ActionInstrumentation.java b/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/ActionInstrumentation.java index 191cc6f838f1..4d376817c6f0 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/ActionInstrumentation.java +++ b/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/ActionInstrumentation.java @@ -71,6 +71,9 @@ public static void stopTraceOnResponse( @Advice.Return(readOnly = false) Future responseFuture, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope) { + if (scope == null) { + return; + } scope.close(); updateSpan(context, req); diff --git a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ActionInstrumentation.java b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ActionInstrumentation.java index 3e0a7bca0de5..dd82526a5875 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ActionInstrumentation.java +++ b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ActionInstrumentation.java @@ -71,6 +71,9 @@ public static void stopTraceOnResponse( @Advice.Return(readOnly = false) Future responseFuture, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope) { + if (scope == null) { + return; + } scope.close(); updateSpan(context, req); diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/ProducerImplInstrumentation.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/ProducerImplInstrumentation.java index 4b782edd6e28..79211824e317 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/ProducerImplInstrumentation.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/ProducerImplInstrumentation.java @@ -14,18 +14,14 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.telemetry.PulsarRequest; -import java.util.concurrent.CompletableFuture; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.apache.pulsar.client.api.Message; -import org.apache.pulsar.client.api.MessageId; import org.apache.pulsar.client.api.PulsarClient; -import org.apache.pulsar.client.impl.MessageImpl; import org.apache.pulsar.client.impl.ProducerImpl; import org.apache.pulsar.client.impl.PulsarClientImpl; import org.apache.pulsar.client.impl.SendCallback; @@ -73,7 +69,7 @@ public static class ProducerSendAsyncMethodAdvice { public static void before( @Advice.This ProducerImpl producer, @Advice.Argument(value = 0) Message message, - @Advice.Argument(value = 1, readOnly = false) SendCallback callback) { + @Advice.Argument(value = 1) SendCallback callback) { Context parent = Context.current(); PulsarRequest request = PulsarRequest.create(message, VirtualFieldStore.extract(producer)); @@ -82,54 +78,9 @@ public static void before( } Context context = producerInstrumenter().start(parent, request); - callback = new SendCallbackWrapper(context, request, callback); - } - } - - public static class SendCallbackWrapper implements SendCallback { - - private final Context context; - private final PulsarRequest request; - private final SendCallback delegate; - - public SendCallbackWrapper(Context context, PulsarRequest request, SendCallback callback) { - this.context = context; - this.request = request; - this.delegate = callback; - } - - @Override - public void sendComplete(Exception e) { - if (context == null) { - this.delegate.sendComplete(e); - return; - } - - try (Scope ignore = context.makeCurrent()) { - this.delegate.sendComplete(e); - } finally { - producerInstrumenter().end(context, request, null, e); - } - } - - @Override - public void addCallback(MessageImpl msg, SendCallback scb) { - this.delegate.addCallback(msg, scb); - } - - @Override - public SendCallback getNextSendCallback() { - return this.delegate.getNextSendCallback(); - } - - @Override - public MessageImpl getNextMessage() { - return this.delegate.getNextMessage(); - } - - @Override - public CompletableFuture getFuture() { - return this.delegate.getFuture(); + // Inject the context/request into the SendCallback. This will be extracted and used when the + // message is sent and the callback is invoked. see `SendCallbackInstrumentation`. + VirtualFieldStore.inject(callback, context, request); } } } diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarInstrumentationModule.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarInstrumentationModule.java index a6ed299c64f6..172538815cfb 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarInstrumentationModule.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarInstrumentationModule.java @@ -24,6 +24,7 @@ public List typeInstrumentations() { new ConsumerImplInstrumentation(), new ProducerImplInstrumentation(), new MessageInstrumentation(), - new MessageListenerInstrumentation()); + new MessageListenerInstrumentation(), + new SendCallbackInstrumentation()); } } diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/SendCallbackData.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/SendCallbackData.java new file mode 100644 index 000000000000..462843d70e31 --- /dev/null +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/SendCallbackData.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pulsar.v2_8; + +import io.opentelemetry.context.Context; +import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.telemetry.PulsarRequest; + +public final class SendCallbackData { + public final Context context; + public final PulsarRequest request; + + private SendCallbackData(Context context, PulsarRequest request) { + this.context = context; + this.request = request; + } + + public static SendCallbackData create(Context context, PulsarRequest request) { + return new SendCallbackData(context, request); + } +} diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/SendCallbackInstrumentation.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/SendCallbackInstrumentation.java new file mode 100644 index 000000000000..85295042e335 --- /dev/null +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/SendCallbackInstrumentation.java @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pulsar.v2_8; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.telemetry.PulsarSingletons.producerInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.telemetry.PulsarRequest; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.pulsar.client.impl.SendCallback; + +public class SendCallbackInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.apache.pulsar.client.impl.SendCallback"); + } + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("org.apache.pulsar.client.impl.SendCallback")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod().and(named("sendComplete")), + SendCallbackInstrumentation.class.getName() + "$SendCallbackSendCompleteAdvice"); + } + + @SuppressWarnings("unused") + public static class SendCallbackSendCompleteAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.This SendCallback callback, + @Advice.Local("otelContext") Context otelContext, + @Advice.Local("otelScope") Scope otelScope, + @Advice.Local("otelRequest") PulsarRequest request) { + // Extract the Context and PulsarRequest from the SendCallback instance. + SendCallbackData callBackData = VirtualFieldStore.extract(callback); + if (callBackData != null) { + // If the extraction was successful, store the Context and PulsarRequest in local variables. + otelContext = callBackData.context; + request = callBackData.request; + otelScope = otelContext.makeCurrent(); + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Argument(0) Throwable t, + @Advice.Local("otelContext") Context otelContext, + @Advice.Local("otelScope") Scope otelScope, + @Advice.Local("otelRequest") PulsarRequest request) { + if (otelScope != null) { + // Close the Scope and end the span. + otelScope.close(); + producerInstrumenter().end(otelContext, request, null, t); + } + } + } +} diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/VirtualFieldStore.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/VirtualFieldStore.java index 193079174c9e..9ec84944feaf 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/VirtualFieldStore.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/VirtualFieldStore.java @@ -7,9 +7,11 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.telemetry.PulsarRequest; import org.apache.pulsar.client.api.Consumer; import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.Producer; +import org.apache.pulsar.client.impl.SendCallback; import org.apache.pulsar.client.impl.TopicMessageImpl; public class VirtualFieldStore { @@ -19,6 +21,8 @@ public class VirtualFieldStore { VirtualField.find(Producer.class, ProducerData.class); private static final VirtualField, String> CONSUMER_FIELD = VirtualField.find(Consumer.class, String.class); + private static final VirtualField CALLBACK_FIELD = + VirtualField.find(SendCallback.class, SendCallbackData.class); private VirtualFieldStore() {} @@ -40,6 +44,12 @@ public static void inject(Consumer instance, String serviceUrl) { CONSUMER_FIELD.set(instance, serviceUrl); } + public static void inject(SendCallback instance, Context context, PulsarRequest request) { + if (instance != null) { + CALLBACK_FIELD.set(instance, SendCallbackData.create(context, request)); + } + } + public static Context extract(Message instance) { if (instance instanceof TopicMessageImpl) { TopicMessageImpl topicMessage = (TopicMessageImpl) instance; @@ -59,4 +69,8 @@ public static ProducerData extract(Producer instance) { public static String extract(Consumer instance) { return CONSUMER_FIELD.get(instance); } + + public static SendCallbackData extract(SendCallback instance) { + return CALLBACK_FIELD.get(instance); + } } diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchMessagingAttributesGetter.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchMessagingAttributesGetter.java index 5a77b09e17b7..7e934de2b666 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchMessagingAttributesGetter.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchMessagingAttributesGetter.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; import javax.annotation.Nullable; +import org.apache.pulsar.common.naming.TopicName; enum PulsarBatchMessagingAttributesGetter implements MessagingAttributesGetter { @@ -81,6 +82,16 @@ public Long getBatchMessageCount(PulsarBatchRequest request, @Nullable Void unus return (long) request.getMessages().size(); } + @Nullable + @Override + public String getDestinationPartitionId(PulsarBatchRequest request) { + int partitionIndex = TopicName.getPartitionIndex(request.getDestination()); + if (partitionIndex == -1) { + return null; + } + return String.valueOf(partitionIndex); + } + @Override public List getMessageHeader(PulsarBatchRequest request, String name) { return StreamSupport.stream(request.getMessages().spliterator(), false) diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchRequest.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchRequest.java index 201c01ed3635..ae1c81cf188c 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchRequest.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarBatchRequest.java @@ -10,6 +10,7 @@ import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.UrlParser.UrlData; import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.Messages; +import org.apache.pulsar.common.naming.TopicName; public final class PulsarBatchRequest extends BasePulsarRequest { private final Messages messages; @@ -30,7 +31,10 @@ private static String getTopicName(Messages messages) { if (topicName == null) { topicName = name; } else if (!topicName.equals(name)) { - return null; + // this is a partitioned topic + // persistent://public/default/test-partition-0 persistent://public/default/test-partition-1 + // return persistent://public/default/test + return TopicName.get(topicName).getPartitionedTopicName(); } } return topicName; diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarMessagingAttributesGetter.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarMessagingAttributesGetter.java index ab7491b6699f..08492480c143 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarMessagingAttributesGetter.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarMessagingAttributesGetter.java @@ -12,6 +12,7 @@ import java.util.List; import javax.annotation.Nullable; import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.common.naming.TopicName; enum PulsarMessagingAttributesGetter implements MessagingAttributesGetter { INSTANCE; @@ -83,6 +84,16 @@ public Long getBatchMessageCount(PulsarRequest request, @Nullable Void unused) { return null; } + @Nullable + @Override + public String getDestinationPartitionId(PulsarRequest request) { + int partitionIndex = TopicName.getPartitionIndex(request.getDestination()); + if (partitionIndex == -1) { + return null; + } + return String.valueOf(partitionIndex); + } + @Override public List getMessageHeader(PulsarRequest request, String name) { String value = request.getMessage().getProperty(name); diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarSingletons.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarSingletons.java index 1ab6aa3f48ac..51d534f8158b 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarSingletons.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/telemetry/PulsarSingletons.java @@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingAttributesGetter; +import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingConsumerMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingProducerMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; @@ -76,6 +77,7 @@ private static Instrumenter createConsumerReceiveInstrument MessagingSpanNameExtractor.create(getter, MessageOperation.RECEIVE)) .addAttributesExtractor( createMessagingAttributesExtractor(getter, MessageOperation.RECEIVE)) + .addOperationMetrics(MessagingConsumerMetrics.get()) .addAttributesExtractor( ServerAttributesExtractor.create(new PulsarNetClientAttributesGetter())); @@ -101,6 +103,7 @@ private static Instrumenter createConsumerBatchReceive .addAttributesExtractor( ServerAttributesExtractor.create(new PulsarNetClientAttributesGetter())) .addSpanLinksExtractor(new PulsarBatchRequestSpanLinksExtractor(PROPAGATOR)) + .addOperationMetrics(MessagingConsumerMetrics.get()) .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } @@ -189,7 +192,7 @@ private static Context startAndEndConsumerReceive( Timer timer, Consumer consumer, Throwable throwable) { - if (messages == null) { + if (messages == null || messages.size() == 0) { return null; } String brokerUrl = VirtualFieldStore.extract(consumer); diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/AbstractPulsarClientTest.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/AbstractPulsarClientTest.java index 7883d5082c4f..d87708e196bb 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/AbstractPulsarClientTest.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/AbstractPulsarClientTest.java @@ -5,24 +5,24 @@ package io.opentelemetry.javaagent.instrumentation.pulsar.v2_8; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_BATCH_MESSAGE_COUNT; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.AttributeKey; 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.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.data.SpanData; import java.time.Duration; @@ -43,6 +43,7 @@ import org.apache.pulsar.client.api.PulsarClientException; import org.apache.pulsar.client.api.Schema; import org.apache.pulsar.client.api.SubscriptionInitialPosition; +import org.apache.pulsar.common.naming.TopicName; import org.assertj.core.api.AbstractLongAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -72,12 +73,12 @@ abstract class AbstractPulsarClientTest { static Consumer consumer; static Producer producer2; - private static String brokerHost; - private static int brokerPort; + static String brokerHost; + static int brokerPort; private static final AttributeKey MESSAGE_TYPE = AttributeKey.stringKey("messaging.pulsar.message.type"); - private static final double[] DURATION_BUCKETS = + static final double[] DURATION_BUCKETS = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0 }; @@ -172,7 +173,24 @@ void testConsumeNonPartitionedTopicUsingBatchReceive() throws Exception { assertThat(testing.metrics()) .satisfiesExactlyInAnyOrder( metric -> - OpenTelemetryAssertions.assertThat(metric) + assertThat(metric) + .hasName("messaging.receive.duration") + .hasUnit("s") + .hasDescription("Measures the duration of receive operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) .hasName("messaging.publish.duration") .hasUnit("s") .hasDescription("Measures the duration of publish operation.") @@ -187,7 +205,23 @@ void testConsumeNonPartitionedTopicUsingBatchReceive() throws Exception { equalTo(MESSAGING_DESTINATION_NAME, topic), equalTo(SERVER_PORT, brokerPort), equalTo(SERVER_ADDRESS, brokerHost)) - .hasBucketBoundaries(DURATION_BUCKETS)))); + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) + .hasName("messaging.receive.messages") + .hasUnit("{message}") + .hasDescription("Measures the number of received messages.") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost))))); } @Test @@ -251,6 +285,59 @@ void testConsumeNonPartitionedTopicUsingBatchReceiveAsync() throws Exception { span.hasName("callback") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(1)))); + + assertThat(testing.metrics()) + .satisfiesExactlyInAnyOrder( + metric -> + assertThat(metric) + .hasName("messaging.receive.duration") + .hasUnit("s") + .hasDescription("Measures the duration of receive operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) + .hasName("messaging.publish.duration") + .hasUnit("s") + .hasDescription("Measures the duration of publish operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) + .hasName("messaging.receive.messages") + .hasUnit("{message}") + .hasDescription("Measures the number of received messages.") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost))))); } static List sendAttributes( @@ -273,6 +360,10 @@ static List sendAttributes( AttributeKey.stringArrayKey("messaging.header.test_message_header"), Collections.singletonList("test"))); } + int partitionIndex = TopicName.getPartitionIndex(destination); + if (partitionIndex != -1) { + assertions.add(equalTo(MESSAGING_DESTINATION_PARTITION_ID, String.valueOf(partitionIndex))); + } return assertions; } @@ -307,6 +398,10 @@ static List receiveAttributes( if (isBatch) { assertions.add(satisfies(MESSAGING_BATCH_MESSAGE_COUNT, AbstractLongAssert::isPositive)); } + int partitionIndex = TopicName.getPartitionIndex(destination); + if (partitionIndex != -1) { + assertions.add(equalTo(MESSAGING_DESTINATION_PARTITION_ID, String.valueOf(partitionIndex))); + } return assertions; } @@ -326,6 +421,10 @@ static List processAttributes( AttributeKey.stringArrayKey("messaging.header.test_message_header"), Collections.singletonList("test"))); } + int partitionIndex = TopicName.getPartitionIndex(destination); + if (partitionIndex != -1) { + assertions.add(equalTo(MESSAGING_DESTINATION_PARTITION_ID, String.valueOf(partitionIndex))); + } return assertions; } diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientTest.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientTest.java index 4ffb30469265..93f13f9ee1d8 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientTest.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientTest.java @@ -7,6 +7,12 @@ import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanKind; import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.data.LinkData; @@ -18,6 +24,7 @@ import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.MessageId; import org.apache.pulsar.client.api.MessageListener; +import org.apache.pulsar.client.api.Messages; import org.apache.pulsar.client.api.Schema; import org.apache.pulsar.client.api.SubscriptionInitialPosition; import org.junit.jupiter.api.Test; @@ -126,6 +133,61 @@ void testConsumeNonPartitionedTopicUsingReceive() throws Exception { .hasLinks(LinkData.create(producerSpan.get().getSpanContext())) .hasAttributesSatisfyingExactly( receiveAttributes(topic, msgId.toString(), false)))); + + assertThat(testing.metrics()) + .satisfiesExactlyInAnyOrder( + metric -> + assertThat(metric) + .hasName("messaging.receive.duration") + .hasUnit("s") + .hasDescription("Measures the duration of receive operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) + .hasName("messaging.receive.messages") + .hasUnit("{message}") + .hasDescription("Measures the number of received messages.") + .hasLongSumSatisfying( + sum -> { + sum.hasPointsSatisfying( + point -> { + point + .hasValue(1) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)); + }); + }), + metric -> + assertThat(metric) + .hasName("messaging.publish.duration") + .hasUnit("s") + .hasDescription("Measures the duration of publish operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS)))); } @Test @@ -185,6 +247,61 @@ void testConsumeNonPartitionedTopicUsingReceiveAsync() throws Exception { span.hasName("callback") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)))); + + assertThat(testing.metrics()) + .satisfiesExactlyInAnyOrder( + metric -> + assertThat(metric) + .hasName("messaging.receive.duration") + .hasUnit("s") + .hasDescription("Measures the duration of receive operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) + .hasName("messaging.receive.messages") + .hasUnit("{message}") + .hasDescription("Measures the number of received messages.") + .hasLongSumSatisfying( + sum -> { + sum.hasPointsSatisfying( + point -> { + point + .hasValue(1) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)); + }); + }), + metric -> + assertThat(metric) + .hasName("messaging.publish.duration") + .hasUnit("s") + .hasDescription("Measures the duration of publish operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS)))); } @Test @@ -231,6 +348,61 @@ void testConsumeNonPartitionedTopicUsingReceiveWithTimeout() throws Exception { .hasLinks(LinkData.create(producerSpan.get().getSpanContext())) .hasAttributesSatisfyingExactly( receiveAttributes(topic, msgId.toString(), false)))); + + assertThat(testing.metrics()) + .satisfiesExactlyInAnyOrder( + metric -> + assertThat(metric) + .hasName("messaging.receive.duration") + .hasUnit("s") + .hasDescription("Measures the duration of receive operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS))), + metric -> + assertThat(metric) + .hasName("messaging.receive.messages") + .hasUnit("{message}") + .hasDescription("Measures the number of received messages.") + .hasLongSumSatisfying( + sum -> { + sum.hasPointsSatisfying( + point -> { + point + .hasValue(1) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)); + }); + }), + metric -> + assertThat(metric) + .hasName("messaging.publish.duration") + .hasUnit("s") + .hasDescription("Measures the duration of publish operation.") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSumGreaterThan(0.0) + .hasAttributesSatisfying( + equalTo(MESSAGING_SYSTEM, "pulsar"), + equalTo(MESSAGING_DESTINATION_NAME, topic), + equalTo(SERVER_PORT, brokerPort), + equalTo(SERVER_ADDRESS, brokerHost)) + .hasBucketBoundaries(DURATION_BUCKETS)))); } @Test @@ -444,4 +616,59 @@ void testConsumeMultiTopics() throws Exception { .hasAttributesSatisfyingExactly( processAttributes(topic2, msgId2.toString(), false)))); } + + @Test + void testConsumePartitionedTopicUsingBatchReceive() throws Exception { + String topic = "persistent://public/default/testConsumePartitionedTopicUsingBatchReceive"; + admin.topics().createPartitionedTopic(topic, 4); + consumer = + client + .newConsumer(Schema.STRING) + .subscriptionName("test_sub") + .topic(topic) + .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest) + .subscribe(); + + producer = client.newProducer(Schema.STRING).topic(topic).enableBatching(false).create(); + + String msg = "test"; + for (int i = 0; i < 10; i++) { + producer.send(msg); + } + + Messages receivedMsg = consumer.batchReceive(); + consumer.acknowledge(receivedMsg); + + assertThat(testing.metrics()) + .satisfiesOnlyOnce( + metric -> + assertThat(metric) + .hasName("messaging.receive.messages") + .hasUnit("{message}") + .hasDescription("Measures the number of received messages.") + .hasLongSumSatisfying( + sum -> { + sum.satisfies( + pointData -> { + pointData + .getPoints() + .forEach( + p -> { + assertThat(p.getValue()).isPositive(); + if (p.getValue() == receivedMsg.size()) { + assertThat( + p.getAttributes() + .get(MESSAGING_DESTINATION_NAME)) + .isEqualTo(topic); + assertThat(p.getAttributes().get(MESSAGING_SYSTEM)) + .isEqualTo("pulsar"); + assertThat(p.getAttributes().get(SERVER_PORT)) + .isEqualTo(brokerPort); + assertThat(p.getAttributes().get(SERVER_ADDRESS)) + .isEqualTo(brokerHost); + } + }); + }); + })); + } } diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts b/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts index 7304d19c9a64..ff63fb854165 100644 --- a/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts @@ -10,6 +10,8 @@ otelJava { dependencies { implementation(enforcedPlatform("io.quarkus.platform:quarkus-bom:2.16.7.Final")) + // fails with junit 5.11.+ + implementation(enforcedPlatform("org.junit:junit-bom:5.10.3")) implementation("io.quarkus:quarkus-resteasy-reactive") testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts b/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts index 1d0b07983039..afd93f30ed41 100644 --- a/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts @@ -16,6 +16,8 @@ if (findProperty("testLatestDeps") as Boolean) { dependencies { implementation(enforcedPlatform("io.quarkus.platform:quarkus-bom:$quarkusVersion")) + // fails with junit 5.11.+ + implementation(enforcedPlatform("org.junit:junit-bom:5.10.3")) implementation("io.quarkus:quarkus-resteasy-reactive") testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java index 89ccfd232a9a..99b4861ac6ae 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java @@ -128,6 +128,9 @@ public static void stopSpan( if (callDepth.decrementAndGet() > 0) { return; } + if (scope == null) { + return; + } scope.close(); diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackInstrumentationModule.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackInstrumentationModule.java index c0530aedfb79..ea02d096b3db 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackInstrumentationModule.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackInstrumentationModule.java @@ -10,22 +10,20 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; @AutoService(InstrumentationModule.class) -public class RatpackInstrumentationModule extends InstrumentationModule { +public class RatpackInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public RatpackInstrumentationModule() { super("ratpack", "ratpack-1.4"); } @Override - public boolean isIndyModule() { - // java.lang.ClassCastException: class - // io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.AutoValue_ServerContext - // cannot be cast to class - // io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.ServerContext - // (io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.AutoValue_ServerContext is in unnamed module of loader 'app'; io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.ServerContext is in unnamed module of loader io.opentelemetry.javaagent.tooling.instrumentation.indy.InstrumentationModuleClassLoader @7f088b5c) - return false; + public String getModuleGroup() { + // relies on netty + return "netty"; } @Override diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java index eb61bbf5f57c..cdfd48657a56 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java @@ -9,12 +9,10 @@ import static io.opentelemetry.javaagent.instrumentation.ratpack.RatpackSingletons.updateServerSpanName; import static io.opentelemetry.javaagent.instrumentation.ratpack.RatpackSingletons.updateSpanNames; -import io.netty.util.Attribute; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; -import java.util.Deque; import ratpack.handling.Context; import ratpack.handling.Handler; @@ -26,10 +24,8 @@ public final class TracingHandler implements Handler { @Override public void handle(Context ctx) { - Attribute> serverContextAttribute = - ctx.getDirectChannelAccess().getChannel().attr(AttributeKeys.SERVER_CONTEXT); - Deque serverContexts = serverContextAttribute.get(); - ServerContext serverContext = serverContexts != null ? serverContexts.peekFirst() : null; + ServerContext serverContext = + ServerContexts.peekFirst(ctx.getDirectChannelAccess().getChannel()); // Must use context from channel, as executor instrumentation is not accurate - Ratpack // internally queues events and then drains them in batches, causing executor instrumentation to diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java index 1a2b729b68ee..8f36953986ac 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java @@ -33,8 +33,8 @@ void shouldHaveDefaultMetrics() { testing.waitAndAssertMetrics( "io.opentelemetry.runtime-telemetry-java17", - metric -> metric.hasName("process.runtime.jvm.cpu.longlock"), - metric -> metric.hasName("process.runtime.jvm.cpu.limit"), - metric -> metric.hasName("process.runtime.jvm.cpu.context_switch")); + metric -> metric.hasName("jvm.cpu.longlock"), + metric -> metric.hasName("jvm.cpu.limit"), + metric -> metric.hasName("jvm.cpu.context_switch")); } } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/README.md b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/README.md index 779a0cecffba..48e144d1209a 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/README.md +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/README.md @@ -35,16 +35,16 @@ default, and the telemetry each produces: -| JfrFeature | Default Enabled | Metrics | -| ------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| BUFFER_METRICS | `false` | `process.runtime.jvm.buffer.count`, `process.runtime.jvm.buffer.limit`, `process.runtime.jvm.buffer.usage` | -| CLASS_LOAD_METRICS | `false` | `process.runtime.jvm.classes.current_loaded`, `process.runtime.jvm.classes.loaded`, `process.runtime.jvm.classes.unloaded` | -| CONTEXT_SWITCH_METRICS | `true` | `process.runtime.jvm.cpu.context_switch` | -| CPU_COUNT_METRICS | `true` | `process.runtime.jvm.cpu.limit` | -| CPU_UTILIZATION_METRICS | `false` | `process.runtime.jvm.cpu.utilization`, `process.runtime.jvm.system.cpu.utilization` | -| GC_DURATION_METRICS | `false` | `process.runtime.jvm.gc.duration` | -| LOCK_METRICS | `true` | `process.runtime.jvm.cpu.longlock` | -| MEMORY_ALLOCATION_METRICS | `true` | `process.runtime.jvm.memory.allocation` | -| MEMORY_POOL_METRICS | `false` | `process.runtime.jvm.memory.committed`, `process.runtime.jvm.memory.init`, `process.runtime.jvm.memory.limit`, `process.runtime.jvm.memory.usage`, `process.runtime.jvm.memory.usage_after_last_gc` | -| NETWORK_IO_METRICS | `true` | `process.runtime.jvm.network.io`, `process.runtime.jvm.network.time` | -| THREAD_METRICS | `false` | `process.runtime.jvm.threads.count` | +| JfrFeature | Default Enabled | Metrics | +|---------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------| +| BUFFER_METRICS | `false` | `jvm.buffer.count`, `jvm.buffer.memory.limit`, `jvm.buffer.memory.usage` | +| CLASS_LOAD_METRICS | `false` | `jvm.class.count`, `jvm.class.loaded`, `jvm.class.unloaded` | +| CONTEXT_SWITCH_METRICS | `true` | `jvm.cpu.context_switch` | +| CPU_COUNT_METRICS | `true` | `jvm.cpu.limit` | +| CPU_UTILIZATION_METRICS | `false` | `jvm.cpu.recent_utilization`, `jvm.system.cpu.utilization` | +| GC_DURATION_METRICS | `false` | `jvm.gc.duration` | +| LOCK_METRICS | `true` | `jvm.cpu.longlock` | +| MEMORY_ALLOCATION_METRICS | `true` | `jvm.memory.allocation` | +| MEMORY_POOL_METRICS | `false` | `jvm.memory.committed`, `jvm.memory.init`, `jvm.memory.limit`, `jvm.memory.used`, `jvm.memory.used_after_last_gc` | +| NETWORK_IO_METRICS | `true` | `jvm.network.io`, `jvm.network.time` | +| THREAD_METRICS | `false` | `jvm.thread.count` | diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/Constants.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/Constants.java index 4752c01fda9b..d4f3e79d38e8 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/Constants.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/Constants.java @@ -18,7 +18,7 @@ private Constants() {} public static final String ONE = "1"; public static final String HERTZ = "Hz"; public static final String BYTES = "By"; - public static final String MILLISECONDS = "ms"; + public static final String SECONDS = "s"; public static final String COMMITTED = "committed"; public static final String RESERVED = "reserved"; public static final String INITIAL_SIZE = "initialSize"; @@ -26,65 +26,67 @@ private Constants() {} public static final String COMMITTED_SIZE = "committedSize"; public static final String RESERVED_SIZE = "reservedSize"; - public static final String DAEMON = "daemon"; public static final String HEAP = "heap"; - public static final String NON_HEAP = "nonheap"; + public static final String NON_HEAP = "non_heap"; public static final String NETWORK_MODE_READ = "read"; public static final String NETWORK_MODE_WRITE = "write"; public static final String DURATION = "duration"; public static final String END_OF_MINOR_GC = "end of minor GC"; public static final String END_OF_MAJOR_GC = "end of major GC"; - public static final String METRIC_NAME_NETWORK_BYTES = "process.runtime.jvm.network.io"; - public static final String METRIC_DESCRIPTION_NETWORK_BYTES = "Network read/write bytes"; - public static final String METRIC_NAME_NETWORK_DURATION = "process.runtime.jvm.network.time"; - public static final String METRIC_DESCRIPTION_NETWORK_DURATION = "Network read/write duration"; - public static final String METRIC_NAME_COMMITTED = "process.runtime.jvm.memory.committed"; - public static final String METRIC_DESCRIPTION_COMMITTED = "Measure of memory committed"; - public static final String METRIC_NAME_MEMORY = "process.runtime.jvm.memory.usage"; - public static final String METRIC_DESCRIPTION_MEMORY = "Measure of memory used"; - public static final String METRIC_NAME_MEMORY_AFTER = - "process.runtime.jvm.memory.usage_after_last_gc"; + public static final String METRIC_NAME_NETWORK_BYTES = "jvm.network.io"; + public static final String METRIC_DESCRIPTION_NETWORK_BYTES = "Network read/write bytes."; + public static final String METRIC_NAME_NETWORK_DURATION = "jvm.network.time"; + public static final String METRIC_DESCRIPTION_NETWORK_DURATION = "Network read/write duration."; + public static final String METRIC_NAME_COMMITTED = "jvm.memory.committed"; + public static final String METRIC_DESCRIPTION_COMMITTED = "Measure of memory committed."; + public static final String METRIC_NAME_MEMORY = "jvm.memory.used"; + public static final String METRIC_DESCRIPTION_MEMORY = "Measure of memory used."; + public static final String METRIC_NAME_MEMORY_AFTER = "jvm.memory.used_after_last_gc"; public static final String METRIC_DESCRIPTION_MEMORY_AFTER = - "Measure of memory used, as measured after the most recent garbage collection event on this pool"; - public static final String METRIC_NAME_MEMORY_ALLOCATION = - "process.runtime.jvm.memory.allocation"; - public static final String METRIC_DESCRIPTION_MEMORY_ALLOCATION = "Allocation"; - public static final String METRIC_NAME_MEMORY_INIT = "process.runtime.jvm.memory.init"; - public static final String METRIC_DESCRIPTION_MEMORY_INIT = "Measure of initial memory requested"; - public static final String METRIC_NAME_MEMORY_LIMIT = "process.runtime.jvm.memory.limit"; - public static final String METRIC_DESCRIPTION_MEMORY_LIMIT = "Measure of max obtainable memory"; - public static final String METRIC_NAME_GC_DURATION = "process.runtime.jvm.gc.duration"; + "Measure of memory used, as measured after the most recent garbage collection event on this pool."; + public static final String METRIC_NAME_MEMORY_ALLOCATION = "jvm.memory.allocation"; + public static final String METRIC_DESCRIPTION_MEMORY_ALLOCATION = + "Measure of memory allocations."; + public static final String METRIC_NAME_MEMORY_INIT = "jvm.memory.init"; + public static final String METRIC_DESCRIPTION_MEMORY_INIT = + "Measure of initial memory requested."; + public static final String METRIC_NAME_MEMORY_LIMIT = "jvm.memory.limit"; + public static final String METRIC_DESCRIPTION_MEMORY_LIMIT = "Measure of max obtainable memory."; + public static final String METRIC_NAME_GC_DURATION = "jvm.gc.duration"; public static final String METRIC_DESCRIPTION_GC_DURATION = - "Duration of JVM garbage collection actions"; + "Duration of JVM garbage collection actions."; public static final AttributeKey ATTR_THREAD_NAME = AttributeKey.stringKey("thread.name"); public static final AttributeKey ATTR_ARENA_NAME = AttributeKey.stringKey("arena"); public static final AttributeKey ATTR_NETWORK_MODE = AttributeKey.stringKey("mode"); - public static final AttributeKey ATTR_TYPE = AttributeKey.stringKey("type"); - public static final AttributeKey ATTR_POOL = AttributeKey.stringKey("pool"); - public static final AttributeKey ATTR_GC = AttributeKey.stringKey("pool"); - public static final AttributeKey ATTR_ACTION = AttributeKey.stringKey("action"); - public static final AttributeKey ATTR_DAEMON = AttributeKey.booleanKey(DAEMON); + public static final AttributeKey ATTR_MEMORY_TYPE = + AttributeKey.stringKey("jvm.memory.type"); + public static final AttributeKey ATTR_MEMORY_POOL = + AttributeKey.stringKey("jvm.memory.pool.name"); + public static final AttributeKey ATTR_GC_NAME = AttributeKey.stringKey("jvm.gc.name"); + public static final AttributeKey ATTR_GC_ACTION = AttributeKey.stringKey("jvm.gc.action"); + public static final AttributeKey ATTR_DAEMON = + AttributeKey.booleanKey("jvm.thread.daemon"); public static final Attributes ATTR_PS_EDEN_SPACE = - Attributes.of(ATTR_TYPE, HEAP, ATTR_POOL, "PS Eden Space"); + Attributes.of(ATTR_MEMORY_TYPE, HEAP, ATTR_MEMORY_POOL, "PS Eden Space"); public static final Attributes ATTR_PS_SURVIVOR_SPACE = - Attributes.of(ATTR_TYPE, HEAP, ATTR_POOL, "PS Survivor Space"); + Attributes.of(ATTR_MEMORY_TYPE, HEAP, ATTR_MEMORY_POOL, "PS Survivor Space"); public static final Attributes ATTR_PS_OLD_GEN = - Attributes.of(ATTR_TYPE, HEAP, ATTR_POOL, "PS Old Gen"); + Attributes.of(ATTR_MEMORY_TYPE, HEAP, ATTR_MEMORY_POOL, "PS Old Gen"); public static final Attributes ATTR_G1_SURVIVOR_SPACE = - Attributes.of(ATTR_TYPE, HEAP, ATTR_POOL, "G1 Survivor Space"); + Attributes.of(ATTR_MEMORY_TYPE, HEAP, ATTR_MEMORY_POOL, "G1 Survivor Space"); public static final Attributes ATTR_G1_EDEN_SPACE = - Attributes.of(ATTR_TYPE, HEAP, ATTR_POOL, "G1 Eden Space"); + Attributes.of(ATTR_MEMORY_TYPE, HEAP, ATTR_MEMORY_POOL, "G1 Eden Space"); public static final Attributes ATTR_METASPACE = - Attributes.of(ATTR_TYPE, NON_HEAP, ATTR_POOL, "Metaspace"); + Attributes.of(ATTR_MEMORY_TYPE, NON_HEAP, ATTR_MEMORY_POOL, "Metaspace"); public static final Attributes ATTR_COMPRESSED_CLASS_SPACE = - Attributes.of(ATTR_TYPE, NON_HEAP, ATTR_POOL, "Compressed Class Space"); + Attributes.of(ATTR_MEMORY_TYPE, NON_HEAP, ATTR_MEMORY_POOL, "Compressed Class Space"); public static final Attributes ATTR_CODE_CACHE = - Attributes.of(ATTR_TYPE, NON_HEAP, ATTR_POOL, "CodeCache"); + Attributes.of(ATTR_MEMORY_TYPE, NON_HEAP, ATTR_MEMORY_POOL, "CodeCache"); - public static final String UNIT_CLASSES = "{classes}"; - public static final String UNIT_THREADS = "{threads}"; - public static final String UNIT_BUFFERS = "{buffers}"; + public static final String UNIT_CLASSES = "{class}"; + public static final String UNIT_THREADS = "{thread}"; + public static final String UNIT_BUFFERS = "{buffer}"; public static final String UNIT_UTILIZATION = "1"; } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtil.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtil.java index ae9cfb1e0c32..ff8ddb1894de 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtil.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtil.java @@ -12,13 +12,12 @@ * any time. */ public final class DurationUtil { - private static final double NANOS_PER_MILLI = 1e6; + private static final double NANOS_PER_SECOND = 1e9; - /** Returns the duration as milliseconds, with fractional part included. */ - @SuppressWarnings("TimeUnitMismatch") - public static double toMillis(Duration duration) { + /** Returns the duration as seconds, with fractional part included. */ + public static double toSeconds(Duration duration) { double epochSecs = (double) duration.getSeconds(); - return epochSecs * 1000 + duration.getNano() / NANOS_PER_MILLI; + return epochSecs + duration.getNano() / NANOS_PER_SECOND; } private DurationUtil() {} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/buffer/DirectBufferStatisticsHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/buffer/DirectBufferStatisticsHandler.java index 22dc831e716c..fa2ad512cc46 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/buffer/DirectBufferStatisticsHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/buffer/DirectBufferStatisticsHandler.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17.internal.buffer; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature; @@ -21,19 +22,21 @@ * any time. */ public final class DirectBufferStatisticsHandler implements RecordedEventHandler { - private static final String METRIC_NAME_USAGE = "process.runtime.jvm.buffer.usage"; - private static final String METRIC_NAME_LIMIT = "process.runtime.jvm.buffer.limit"; - private static final String METRIC_NAME_COUNT = "process.runtime.jvm.buffer.count"; - private static final String METRIC_DESCRIPTION_USAGE = "Measure of memory used by buffers"; + private static final String METRIC_NAME_USAGE = "jvm.buffer.memory.usage"; + private static final String METRIC_NAME_LIMIT = "jvm.buffer.memory.limit"; + private static final String METRIC_NAME_COUNT = "jvm.buffer.count"; + private static final String METRIC_DESCRIPTION_USAGE = "Measure of memory used by buffers."; private static final String METRIC_DESCRIPTION_LIMIT = - "Measure of total memory capacity of buffers"; - private static final String METRIC_DESCRIPTION_COUNT = "Number of buffers in the pool"; + "Measure of total memory capacity of buffers."; + private static final String METRIC_DESCRIPTION_COUNT = "Number of buffers in the pool."; private static final String COUNT = "count"; private static final String MAX_CAPACITY = "maxCapacity"; private static final String MEMORY_USED = "memoryUsed"; private static final String EVENT_NAME = "jdk.DirectBufferStatistics"; - private static final Attributes ATTR = Attributes.of(Constants.ATTR_POOL, "direct"); + public static final AttributeKey ATTR_BUFFER_POOL = + AttributeKey.stringKey("jvm.buffer.pool.name"); + private static final Attributes ATTR = Attributes.of(ATTR_BUFFER_POOL, "direct"); private final List observables = new ArrayList<>(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/classes/ClassesLoadedHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/classes/ClassesLoadedHandler.java index f2a38c60da13..98bee7942725 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/classes/ClassesLoadedHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/classes/ClassesLoadedHandler.java @@ -21,25 +21,25 @@ */ public final class ClassesLoadedHandler implements RecordedEventHandler { /** - * process.runtime.jvm.classes.loaded is the total number of classes loaded since JVM start. See: + * jvm.class.loaded is the total number of classes loaded since JVM start. See: * https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md */ - private static final String METRIC_NAME_LOADED = "process.runtime.jvm.classes.loaded"; + private static final String METRIC_NAME_LOADED = "jvm.class.loaded"; - private static final String METRIC_NAME_UNLOADED = "process.runtime.jvm.classes.unloaded"; + private static final String METRIC_NAME_UNLOADED = "jvm.class.unloaded"; /** - * process.runtime.jvm.classes.current_loaded is the number of classes loaded at the time of - * jdk.ClassLoadingStatistics event emission. + * jvm.class.count is the number of classes loaded at the time of jdk.ClassLoadingStatistics event + * emission. */ - private static final String METRIC_NAME_CURRENT = "process.runtime.jvm.classes.current_loaded"; + private static final String METRIC_NAME_CURRENT = "jvm.class.count"; private static final String EVENT_NAME = "jdk.ClassLoadingStatistics"; - private static final String METRIC_DESCRIPTION_CURRENT = "Number of classes currently loaded"; + private static final String METRIC_DESCRIPTION_CURRENT = "Number of classes currently loaded."; private static final String METRIC_DESCRIPTION_LOADED = - "Number of classes loaded since JVM start"; + "Number of classes loaded since JVM start."; private static final String METRIC_DESCRIPTION_UNLOADED = - "Number of classes unloaded since JVM start"; + "Number of classes unloaded since JVM start."; private final List observables = new ArrayList<>(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/container/ContainerConfigurationHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/container/ContainerConfigurationHandler.java index 4af00f9f7834..09220d3c6beb 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/container/ContainerConfigurationHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/container/ContainerConfigurationHandler.java @@ -18,7 +18,7 @@ * any time. */ public final class ContainerConfigurationHandler implements RecordedEventHandler { - private static final String METRIC_NAME = "process.runtime.jvm.cpu.limit"; + private static final String METRIC_NAME = "jvm.cpu.limit"; private static final String EVENT_NAME = "jdk.ContainerConfiguration"; private static final String EFFECTIVE_CPU_COUNT = "effectiveCpuCount"; diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/ContextSwitchRateHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/ContextSwitchRateHandler.java index e9a84387544f..39a3dc6efa7c 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/ContextSwitchRateHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/ContextSwitchRateHandler.java @@ -20,7 +20,7 @@ * any time. */ public final class ContextSwitchRateHandler implements RecordedEventHandler { - private static final String METRIC_NAME = "process.runtime.jvm.cpu.context_switch"; + private static final String METRIC_NAME = "jvm.cpu.context_switch"; private static final String EVENT_NAME = "jdk.ThreadContextSwitchRate"; private final List observables = new ArrayList<>(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/LongLockHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/LongLockHandler.java index 27f9700434f8..8eb26fc9f67b 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/LongLockHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/LongLockHandler.java @@ -23,7 +23,7 @@ * any time. */ public final class LongLockHandler extends AbstractThreadDispatchingHandler { - private static final String METRIC_NAME = "process.runtime.jvm.cpu.longlock"; + private static final String METRIC_NAME = "jvm.cpu.longlock"; private static final String METRIC_DESCRIPTION = "Long lock times"; private static final String EVENT_NAME = "jdk.JavaMonitorWait"; @@ -35,7 +35,7 @@ public LongLockHandler(Meter meter, ThreadGrouper grouper) { meter .histogramBuilder(METRIC_NAME) .setDescription(METRIC_DESCRIPTION) - .setUnit(Constants.MILLISECONDS) + .setUnit(Constants.SECONDS) .build(); } @@ -73,7 +73,7 @@ public PerThreadLongLockHandler(DoubleHistogram histogram, String threadName) { @Override public void accept(RecordedEvent recordedEvent) { if (recordedEvent.hasField(EVENT_THREAD)) { - histogram.record(DurationUtil.toMillis(recordedEvent.getDuration()), attributes); + histogram.record(DurationUtil.toSeconds(recordedEvent.getDuration()), attributes); } // What about the class name in MONITOR_CLASS ? // We can get a stack trace from the thread on the event diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/OverallCpuLoadHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/OverallCpuLoadHandler.java index 54c423b28ae7..260b26857c9e 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/OverallCpuLoadHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/cpu/OverallCpuLoadHandler.java @@ -20,11 +20,12 @@ * any time. */ public final class OverallCpuLoadHandler implements RecordedEventHandler { - private static final String METRIC_NAME_PROCESS = "process.runtime.jvm.cpu.utilization"; - private static final String METRIC_NAME_MACHINE = "process.runtime.jvm.system.cpu.utilization"; - private static final String METRIC_DESCRIPTION_PROCESS = "Recent CPU utilization for the process"; + private static final String METRIC_NAME_PROCESS = "jvm.cpu.recent_utilization"; + private static final String METRIC_NAME_MACHINE = "jvm.system.cpu.utilization"; + private static final String METRIC_DESCRIPTION_PROCESS = + "Recent CPU utilization for the process as reported by the JVM."; private static final String METRIC_DESCRIPTION_MACHINE = - "Recent CPU utilization for the whole system"; + "Recent CPU utilization for the whole system as reported by the JVM."; private static final String EVENT_NAME = "jdk.CPULoad"; private static final String JVM_USER = "jvmUser"; diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/G1GarbageCollectionHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/G1GarbageCollectionHandler.java index e67f8fb1ba46..cc5720df2fdb 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/G1GarbageCollectionHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/G1GarbageCollectionHandler.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17.internal.garbagecollection; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature; import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants; @@ -23,25 +23,24 @@ public final class G1GarbageCollectionHandler implements RecordedEventHandler { private static final String EVENT_NAME = "jdk.G1GarbageCollection"; private static final Attributes ATTR = Attributes.of( - Constants.ATTR_GC, + Constants.ATTR_GC_NAME, "G1 Young Generation", - Constants.ATTR_ACTION, + Constants.ATTR_GC_ACTION, Constants.END_OF_MINOR_GC); - private final LongHistogram histogram; + private final DoubleHistogram histogram; public G1GarbageCollectionHandler(Meter meter) { histogram = meter .histogramBuilder(Constants.METRIC_NAME_GC_DURATION) .setDescription(Constants.METRIC_DESCRIPTION_GC_DURATION) - .setUnit(Constants.MILLISECONDS) - .ofLongs() + .setUnit(Constants.SECONDS) .build(); } @Override public void accept(RecordedEvent ev) { - histogram.record(ev.getLong(Constants.DURATION), ATTR); + histogram.record(ev.getDouble(Constants.DURATION), ATTR); } @Override diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/OldGarbageCollectionHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/OldGarbageCollectionHandler.java index c402450fa73a..26bff6b82108 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/OldGarbageCollectionHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/OldGarbageCollectionHandler.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17.internal.garbagecollection; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature; import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants; @@ -22,7 +22,7 @@ public final class OldGarbageCollectionHandler implements RecordedEventHandler { private static final String EVENT_NAME = "jdk.OldGarbageCollection"; - private final LongHistogram histogram; + private final DoubleHistogram histogram; private final Attributes attributes; public OldGarbageCollectionHandler(Meter meter, String gc) { @@ -30,17 +30,17 @@ public OldGarbageCollectionHandler(Meter meter, String gc) { meter .histogramBuilder(Constants.METRIC_NAME_GC_DURATION) .setDescription(Constants.METRIC_DESCRIPTION_GC_DURATION) - .setUnit(Constants.MILLISECONDS) - .ofLongs() + .setUnit(Constants.SECONDS) .build(); // Set the attribute's GC based on which GC is being used. attributes = - Attributes.of(Constants.ATTR_GC, gc, Constants.ATTR_ACTION, Constants.END_OF_MAJOR_GC); + Attributes.of( + Constants.ATTR_GC_NAME, gc, Constants.ATTR_GC_ACTION, Constants.END_OF_MAJOR_GC); } @Override public void accept(RecordedEvent ev) { - histogram.record(ev.getLong(Constants.DURATION), attributes); + histogram.record(ev.getDouble(Constants.DURATION), attributes); } @Override diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/YoungGarbageCollectionHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/YoungGarbageCollectionHandler.java index 88d8a1915bfd..534b131de2ee 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/YoungGarbageCollectionHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/garbagecollection/YoungGarbageCollectionHandler.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17.internal.garbagecollection; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature; import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants; @@ -22,7 +22,7 @@ public final class YoungGarbageCollectionHandler implements RecordedEventHandler { private static final String EVENT_NAME = "jdk.YoungGarbageCollection"; - private final LongHistogram histogram; + private final DoubleHistogram histogram; private final Attributes attributes; public YoungGarbageCollectionHandler(Meter meter, String gc) { @@ -30,18 +30,18 @@ public YoungGarbageCollectionHandler(Meter meter, String gc) { meter .histogramBuilder(Constants.METRIC_NAME_GC_DURATION) .setDescription(Constants.METRIC_DESCRIPTION_GC_DURATION) - .setUnit(Constants.MILLISECONDS) - .ofLongs() + .setUnit(Constants.SECONDS) .build(); // Set the attribute's GC based on which GC is being used. // G1 young collection is already handled by G1GarbageCollectionHandler. attributes = - Attributes.of(Constants.ATTR_GC, gc, Constants.ATTR_ACTION, Constants.END_OF_MINOR_GC); + Attributes.of( + Constants.ATTR_GC_NAME, gc, Constants.ATTR_GC_ACTION, Constants.END_OF_MINOR_GC); } @Override public void accept(RecordedEvent ev) { - histogram.record(ev.getLong(Constants.DURATION), attributes); + histogram.record(ev.getDouble(Constants.DURATION), attributes); } @Override diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/CodeCacheConfigurationHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/CodeCacheConfigurationHandler.java index d96387941d5f..1b11c80ea4d1 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/CodeCacheConfigurationHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/CodeCacheConfigurationHandler.java @@ -24,7 +24,8 @@ public final class CodeCacheConfigurationHandler implements RecordedEventHandler private static final String EVENT_NAME = "jdk.CodeCacheConfiguration"; private static final Attributes ATTR = - Attributes.of(Constants.ATTR_TYPE, Constants.NON_HEAP, Constants.ATTR_POOL, "CodeCache"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, Constants.NON_HEAP, Constants.ATTR_MEMORY_POOL, "CodeCache"); private final List observables = new ArrayList<>(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/G1HeapSummaryHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/G1HeapSummaryHandler.java index 3b850b128d0c..05c8a49fb5cc 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/G1HeapSummaryHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/G1HeapSummaryHandler.java @@ -32,9 +32,14 @@ public final class G1HeapSummaryHandler implements RecordedEventHandler { private static final String SURVIVOR_USED_SIZE = "survivorUsedSize"; private static final String WHEN = "when"; private static final Attributes ATTR_MEMORY_EDEN = - Attributes.of(Constants.ATTR_TYPE, Constants.HEAP, Constants.ATTR_POOL, "G1 Eden Space"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, Constants.HEAP, Constants.ATTR_MEMORY_POOL, "G1 Eden Space"); private static final Attributes ATTR_MEMORY_SURVIVOR = - Attributes.of(Constants.ATTR_TYPE, Constants.HEAP, Constants.ATTR_POOL, "G1 Survivor Space"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, + Constants.HEAP, + Constants.ATTR_MEMORY_POOL, + "G1 Survivor Space"); // private static final Attributes ATTR_MEMORY_OLD_USED = // Attributes.of(ATTR_TYPE, HEAP, ATTR_POOL, "G1 Old Gen"); // TODO needs jdk JFR support diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/MetaspaceSummaryHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/MetaspaceSummaryHandler.java index f02c989f47fc..fcbe93cafbab 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/MetaspaceSummaryHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/MetaspaceSummaryHandler.java @@ -28,10 +28,14 @@ public final class MetaspaceSummaryHandler implements RecordedEventHandler { private static final String EVENT_NAME = "jdk.MetaspaceSummary"; private static final Attributes ATTR_MEMORY_METASPACE = - Attributes.of(Constants.ATTR_TYPE, Constants.NON_HEAP, Constants.ATTR_POOL, "Metaspace"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, Constants.NON_HEAP, Constants.ATTR_MEMORY_POOL, "Metaspace"); private static final Attributes ATTR_MEMORY_COMPRESSED_CLASS_SPACE = Attributes.of( - Constants.ATTR_TYPE, Constants.NON_HEAP, Constants.ATTR_POOL, "Compressed Class Space"); + Constants.ATTR_MEMORY_TYPE, + Constants.NON_HEAP, + Constants.ATTR_MEMORY_POOL, + "Compressed Class Space"); private final List observables = new ArrayList<>(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/ParallelHeapSummaryHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/ParallelHeapSummaryHandler.java index c187f1d9401c..3ec8e57c845d 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/ParallelHeapSummaryHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/memory/ParallelHeapSummaryHandler.java @@ -33,11 +33,17 @@ public final class ParallelHeapSummaryHandler implements RecordedEventHandler { private static final String WHEN = "when"; private static final String SIZE = "size"; private static final Attributes ATTR_MEMORY_EDEN = - Attributes.of(Constants.ATTR_TYPE, Constants.HEAP, Constants.ATTR_POOL, "PS Eden Space"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, Constants.HEAP, Constants.ATTR_MEMORY_POOL, "PS Eden Space"); private static final Attributes ATTR_MEMORY_SURVIVOR = - Attributes.of(Constants.ATTR_TYPE, Constants.HEAP, Constants.ATTR_POOL, "PS Survivor Space"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, + Constants.HEAP, + Constants.ATTR_MEMORY_POOL, + "PS Survivor Space"); private static final Attributes ATTR_MEMORY_OLD = - Attributes.of(Constants.ATTR_TYPE, Constants.HEAP, Constants.ATTR_POOL, "PS Old Gen"); + Attributes.of( + Constants.ATTR_MEMORY_TYPE, Constants.HEAP, Constants.ATTR_MEMORY_POOL, "PS Old Gen"); private final List observables = new ArrayList<>(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkReadHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkReadHandler.java index 4764a09a49e3..b1b1a6eee005 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkReadHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkReadHandler.java @@ -40,7 +40,7 @@ public NetworkReadHandler(Meter meter, ThreadGrouper nameNormalizer) { meter .histogramBuilder(Constants.METRIC_NAME_NETWORK_DURATION) .setDescription(Constants.METRIC_DESCRIPTION_NETWORK_DURATION) - .setUnit(Constants.MILLISECONDS) + .setUnit(Constants.SECONDS) .build(); } @@ -81,7 +81,7 @@ public PerThreadNetworkReadHandler( @Override public void accept(RecordedEvent ev) { bytesHistogram.record(ev.getLong(BYTES_READ), attributes); - durationHistogram.record(DurationUtil.toMillis(ev.getDuration()), attributes); + durationHistogram.record(DurationUtil.toSeconds(ev.getDuration()), attributes); } } } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkWriteHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkWriteHandler.java index 792cba803014..4de58802fdcf 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkWriteHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/network/NetworkWriteHandler.java @@ -58,7 +58,7 @@ public NetworkWriteHandler(Meter meter, ThreadGrouper nameNormalizer) { meter .histogramBuilder(Constants.METRIC_NAME_NETWORK_DURATION) .setDescription(Constants.METRIC_DESCRIPTION_NETWORK_DURATION) - .setUnit(Constants.MILLISECONDS) + .setUnit(Constants.SECONDS) .build(); } @@ -99,7 +99,7 @@ private PerThreadNetworkWriteHandler( @Override public void accept(RecordedEvent ev) { bytesHistogram.record(ev.getLong(BYTES_WRITTEN), attributes); - durationHistogram.record(DurationUtil.toMillis(ev.getDuration()), attributes); + durationHistogram.record(DurationUtil.toSeconds(ev.getDuration()), attributes); } } } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/threads/ThreadCountHandler.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/threads/ThreadCountHandler.java index 8d5cc57a99fc..ef741482cc3f 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/threads/ThreadCountHandler.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/threads/ThreadCountHandler.java @@ -21,7 +21,7 @@ * any time. */ public final class ThreadCountHandler implements RecordedEventHandler { - private static final String METRIC_NAME = "process.runtime.jvm.threads.count"; + private static final String METRIC_NAME = "jvm.thread.count"; private static final String EVENT_NAME = "jdk.JavaThreadStatistics"; private static final String METRIC_DESCRIPTION = "Number of executing threads"; private static final Attributes ATTR_DAEMON_TRUE = Attributes.of(Constants.ATTR_DAEMON, true); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/BufferMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/BufferMetricTest.java index bfce75d5ab94..c794891ecb06 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/BufferMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/BufferMetricTest.java @@ -5,11 +5,11 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_POOL; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.BYTES; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.UNIT_BUFFERS; import static org.assertj.core.api.Assertions.assertThat; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -39,12 +39,13 @@ void shouldHaveJfrLoadedClassesCountEvents() { ByteBuffer buffer = ByteBuffer.allocateDirect(10000); buffer.put("test".getBytes(StandardCharsets.UTF_8)); - Attributes directBuffer = Attributes.of(ATTR_POOL, "direct"); + AttributeKey attrBufferPool = AttributeKey.stringKey("jvm.buffer.pool.name"); + Attributes directBuffer = Attributes.of(attrBufferPool, "direct"); jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.buffer.count") - .hasDescription("Number of buffers in the pool") + .hasName("jvm.buffer.count") + .hasDescription("Number of buffers in the pool.") .hasUnit(UNIT_BUFFERS) .hasLongSumSatisfying( sum -> @@ -57,8 +58,8 @@ void shouldHaveJfrLoadedClassesCountEvents() { }))), metric -> metric - .hasName("process.runtime.jvm.buffer.limit") - .hasDescription("Measure of total memory capacity of buffers") + .hasName("jvm.buffer.memory.limit") + .hasDescription("Measure of total memory capacity of buffers.") .hasUnit(BYTES) .hasLongSumSatisfying( sum -> @@ -71,8 +72,8 @@ void shouldHaveJfrLoadedClassesCountEvents() { }))), metric -> metric - .hasName("process.runtime.jvm.buffer.usage") - .hasDescription("Measure of memory used by buffers") + .hasName("jvm.buffer.memory.usage") + .hasDescription("Measure of memory used by buffers.") .hasUnit(BYTES) .hasLongSumSatisfying( sum -> diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/G1GcMemoryMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/G1GcMemoryMetricTest.java index b9d5c39ff600..f341d8920b93 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/G1GcMemoryMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/G1GcMemoryMetricTest.java @@ -5,10 +5,10 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_ACTION; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_G1_EDEN_SPACE; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_G1_SURVIVOR_SPACE; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC_ACTION; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC_NAME; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.BYTES; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.END_OF_MAJOR_GC; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.END_OF_MINOR_GC; @@ -16,9 +16,11 @@ import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_GC_DURATION; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_MEMORY; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_MEMORY_AFTER; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_COMMITTED; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_GC_DURATION; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_MEMORY; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_MEMORY_AFTER; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.MILLISECONDS; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; @@ -42,7 +44,7 @@ void shouldHaveMemoryMetrics() { System.gc(); // Test to make sure there's metric data for both eden and survivor spaces. // TODO: once G1 old gen usage added to jdk.G1HeapSummary (in JDK 21), test for it here too. - // TODO: needs JFR support for process.runtime.jvm.memory.limit. + // TODO: needs JFR support for jvm.memory.limit. jfrExtension.waitAndAssertMetrics( metric -> metric @@ -52,7 +54,7 @@ void shouldHaveMemoryMetrics() { .satisfies(G1GcMemoryMetricTest::hasGcAttributes), metric -> metric - .hasName("process.runtime.jvm.memory.committed") + .hasName(METRIC_NAME_COMMITTED) .hasUnit(BYTES) .hasDescription(METRIC_DESCRIPTION_COMMITTED) // TODO: need JFR support for the other G1 pools @@ -79,14 +81,14 @@ void shouldHaveGcDurationMetrics() { // TODO: Need a reliable way to test old and young gen GC in isolation. System.gc(); Attributes minorGcAttributes = - Attributes.of(ATTR_GC, "G1 Young Generation", ATTR_ACTION, END_OF_MINOR_GC); + Attributes.of(ATTR_GC_NAME, "G1 Young Generation", ATTR_GC_ACTION, END_OF_MINOR_GC); Attributes majorGcAttributes = - Attributes.of(ATTR_GC, "G1 Old Generation", ATTR_ACTION, END_OF_MAJOR_GC); + Attributes.of(ATTR_GC_NAME, "G1 Old Generation", ATTR_GC_ACTION, END_OF_MAJOR_GC); jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.gc.duration") - .hasUnit(MILLISECONDS) + .hasName(METRIC_NAME_GC_DURATION) + .hasUnit(SECONDS) .hasDescription(METRIC_DESCRIPTION_GC_DURATION) .satisfies( data -> diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrClassesLoadedCountTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrClassesLoadedCountTest.java index a8b39a07f634..4e5eb144be30 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrClassesLoadedCountTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrClassesLoadedCountTest.java @@ -25,8 +25,8 @@ void shouldHaveJfrLoadedClassesCountEvents() throws Exception { jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.classes.loaded") - .hasDescription("Number of classes loaded since JVM start") + .hasName("jvm.class.loaded") + .hasDescription("Number of classes loaded since JVM start.") .hasUnit(UNIT_CLASSES) .hasLongSumSatisfying( sum -> @@ -38,8 +38,8 @@ void shouldHaveJfrLoadedClassesCountEvents() throws Exception { .isGreaterThanOrEqualTo(0)))), metric -> metric - .hasName("process.runtime.jvm.classes.current_loaded") - .hasDescription("Number of classes currently loaded") + .hasName("jvm.class.count") + .hasDescription("Number of classes currently loaded.") .hasUnit(UNIT_CLASSES) .hasLongSumSatisfying( sum -> @@ -51,8 +51,8 @@ void shouldHaveJfrLoadedClassesCountEvents() throws Exception { .isGreaterThanOrEqualTo(0)))), metric -> metric - .hasName("process.runtime.jvm.classes.unloaded") - .hasDescription("Number of classes unloaded since JVM start") + .hasName("jvm.class.unloaded") + .hasDescription("Number of classes unloaded since JVM start.") .hasUnit(UNIT_CLASSES) .hasLongSumSatisfying( sum -> diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrCpuLockTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrCpuLockTest.java index 0e10b500d770..ce16fc528457 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrCpuLockTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrCpuLockTest.java @@ -5,7 +5,7 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.MILLISECONDS; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import java.time.Duration; @@ -50,8 +50,8 @@ void shouldHaveLockEvents() throws Exception { jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.cpu.longlock") - .hasUnit(MILLISECONDS) + .hasName("jvm.cpu.longlock") + .hasUnit(SECONDS) .hasHistogramSatisfying(histogram -> {})); } } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrOverallCpuLoadHandlerTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrOverallCpuLoadHandlerTest.java index cd18f1ddef99..38c8585a50d0 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrOverallCpuLoadHandlerTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrOverallCpuLoadHandlerTest.java @@ -23,15 +23,16 @@ void shouldHaveCpuLoadEvents() { jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.cpu.utilization") + .hasName("jvm.cpu.recent_utilization") .hasUnit(UNIT_UTILIZATION) - .hasDescription("Recent CPU utilization for the process") + .hasDescription("Recent CPU utilization for the process as reported by the JVM.") .hasDoubleGaugeSatisfying(gauge -> {}), metric -> metric - .hasName("process.runtime.jvm.system.cpu.utilization") + .hasName("jvm.system.cpu.utilization") .hasUnit(UNIT_UTILIZATION) - .hasDescription("Recent CPU utilization for the whole system") + .hasDescription( + "Recent CPU utilization for the whole system as reported by the JVM.") .hasDoubleGaugeSatisfying(gauge -> {})); } } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrThreadCountTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrThreadCountTest.java index 6c19780b5675..38423a9b6105 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrThreadCountTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrThreadCountTest.java @@ -5,11 +5,10 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.DAEMON; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_DAEMON; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.UNIT_THREADS; import static org.assertj.core.api.Assertions.assertThat; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.metrics.data.LongPointData; import java.util.Objects; import org.junit.jupiter.api.Test; @@ -47,7 +46,7 @@ void shouldHaveJfrThreadCountEvents() throws Exception { jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.threads.count") + .hasName("jvm.thread.count") .hasUnit(UNIT_THREADS) .satisfies( data -> @@ -57,6 +56,6 @@ void shouldHaveJfrThreadCountEvents() throws Exception { } private static boolean isDaemon(LongPointData p) { - return Objects.requireNonNull(p.getAttributes().get(AttributeKey.booleanKey(DAEMON))); + return Objects.requireNonNull(p.getAttributes().get(ATTR_DAEMON)); } } diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryCommittedMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryCommittedMetricTest.java index 2a553aab0def..768fca11771a 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryCommittedMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryCommittedMetricTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_METASPACE; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.BYTES; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_COMMITTED; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_COMMITTED; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; @@ -27,7 +28,7 @@ void shouldHaveMemoryCommittedMetrics() { jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.memory.committed") + .hasName(METRIC_NAME_COMMITTED) .hasUnit(BYTES) .hasDescription(METRIC_DESCRIPTION_COMMITTED) .satisfies( diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryLimitMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryLimitMetricTest.java index 4332e60549d5..4ef4f41f6b3d 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryLimitMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryLimitMetricTest.java @@ -8,6 +8,7 @@ import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_COMPRESSED_CLASS_SPACE; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.BYTES; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_MEMORY_LIMIT; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_MEMORY_LIMIT; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; @@ -26,7 +27,7 @@ void shouldHaveMemoryLimitMetrics() { jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.memory.limit") + .hasName(METRIC_NAME_MEMORY_LIMIT) .hasUnit(BYTES) .hasDescription(METRIC_DESCRIPTION_MEMORY_LIMIT) .satisfies( diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryUsageMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryUsageMetricTest.java index b5659d491f40..004115d11c33 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryUsageMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/MetaspaceMemoryUsageMetricTest.java @@ -22,7 +22,7 @@ class MetaspaceMemoryUsageMetricTest { new JfrExtension( builder -> builder.disableAllFeatures().enableFeature(JfrFeature.MEMORY_POOL_METRICS)); - /** This is a basic test for process.runtime.jvm.memory.usage. */ + /** This is a basic test for jvm.memory.used. */ @Test void shouldHaveMemoryUsageMetrics() { System.gc(); diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/PsGcMemoryMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/PsGcMemoryMetricTest.java index d0aa14646b6e..eaba1c4ff9c4 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/PsGcMemoryMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/PsGcMemoryMetricTest.java @@ -5,8 +5,8 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_ACTION; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC_ACTION; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC_NAME; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_PS_EDEN_SPACE; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_PS_OLD_GEN; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_PS_SURVIVOR_SPACE; @@ -18,9 +18,12 @@ import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_MEMORY; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_MEMORY_AFTER; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_MEMORY_LIMIT; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_COMMITTED; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_GC_DURATION; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_MEMORY; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_MEMORY_AFTER; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.MILLISECONDS; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_MEMORY_LIMIT; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; @@ -51,13 +54,13 @@ void shouldHaveMemoryMetrics() { .satisfies(PsGcMemoryMetricTest::hasGcAttributes), metric -> metric - .hasName("process.runtime.jvm.memory.committed") + .hasName(METRIC_NAME_COMMITTED) .hasUnit(BYTES) .hasDescription(METRIC_DESCRIPTION_COMMITTED) .satisfies(PsGcMemoryMetricTest::hasGcAttributes), metric -> metric - .hasName("process.runtime.jvm.memory.limit") + .hasName(METRIC_NAME_MEMORY_LIMIT) .hasUnit(BYTES) .hasDescription(METRIC_DESCRIPTION_MEMORY_LIMIT) .satisfies(PsGcMemoryMetricTest::hasGcAttributes), @@ -83,14 +86,14 @@ void shouldHaveGcDurationMetrics() { System.gc(); Attributes minorGcAttributes = - Attributes.of(ATTR_GC, "PS Scavenge", ATTR_ACTION, END_OF_MINOR_GC); + Attributes.of(ATTR_GC_NAME, "PS Scavenge", ATTR_GC_ACTION, END_OF_MINOR_GC); Attributes majorGcAttributes = - Attributes.of(ATTR_GC, "PS MarkSweep", ATTR_ACTION, END_OF_MAJOR_GC); + Attributes.of(ATTR_GC_NAME, "PS MarkSweep", ATTR_GC_ACTION, END_OF_MAJOR_GC); jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.gc.duration") - .hasUnit(MILLISECONDS) + .hasName(METRIC_NAME_GC_DURATION) + .hasUnit(SECONDS) .hasDescription(METRIC_DESCRIPTION_GC_DURATION) .satisfies( data -> diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/SerialGcMemoryMetricTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/SerialGcMemoryMetricTest.java index 2f0f82ea3cc0..b8e51bc1b99c 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/SerialGcMemoryMetricTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/SerialGcMemoryMetricTest.java @@ -5,12 +5,13 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_ACTION; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC_ACTION; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.ATTR_GC_NAME; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.END_OF_MAJOR_GC; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.END_OF_MINOR_GC; import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_DESCRIPTION_GC_DURATION; -import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.MILLISECONDS; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.METRIC_NAME_GC_DURATION; +import static io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; @@ -34,14 +35,15 @@ void shouldHaveGcDurationMetrics() { // TODO: Need a reliable way to test old and young gen GC in isolation. // Generate some JFR events System.gc(); - Attributes minorGcAttributes = Attributes.of(ATTR_GC, "Copy", ATTR_ACTION, END_OF_MINOR_GC); + Attributes minorGcAttributes = + Attributes.of(ATTR_GC_NAME, "Copy", ATTR_GC_ACTION, END_OF_MINOR_GC); Attributes majorGcAttributes = - Attributes.of(ATTR_GC, "MarkSweepCompact", ATTR_ACTION, END_OF_MAJOR_GC); + Attributes.of(ATTR_GC_NAME, "MarkSweepCompact", ATTR_GC_ACTION, END_OF_MAJOR_GC); jfrExtension.waitAndAssertMetrics( metric -> metric - .hasName("process.runtime.jvm.gc.duration") - .hasUnit(MILLISECONDS) + .hasName(METRIC_NAME_GC_DURATION) + .hasUnit(SECONDS) .hasDescription(METRIC_DESCRIPTION_GC_DURATION) .satisfies( data -> diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtilTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtilTest.java new file mode 100644 index 000000000000..589bd85df1ca --- /dev/null +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/internal/DurationUtilTest.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.runtimemetrics.java17.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; +import org.junit.jupiter.api.Test; + +class DurationUtilTest { + + @Test + void shouldConvertDurationToSeconds() { + // Given + Duration duration = Duration.ofSeconds(7, 144); + + // When + double seconds = DurationUtil.toSeconds(duration); + + // Then + assertEquals(7.000000144, seconds); + } +} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/testing/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/testing/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerTest.java index f49aba1cbe40..4ce7cbea0a69 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/testing/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/testing/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerTest.java @@ -12,7 +12,7 @@ import static io.opentelemetry.instrumentation.javaagent.runtimemetrics.java8.JarAnalyzer.PACKAGE_PATH; import static io.opentelemetry.instrumentation.javaagent.runtimemetrics.java8.JarAnalyzer.PACKAGE_TYPE; import static io.opentelemetry.instrumentation.javaagent.runtimemetrics.java8.JarAnalyzer.PACKAGE_VERSION; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -25,7 +25,6 @@ import io.opentelemetry.api.incubator.events.EventLogger; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import java.io.File; import java.net.MalformedURLException; import java.net.URL; @@ -52,8 +51,7 @@ void processUrl_EmitsEvents(URL archiveUrl, Consumer attribute ArgumentCaptor attributesArgumentCaptor = ArgumentCaptor.forClass(Attributes.class); verify(builder).setAttributes(attributesArgumentCaptor.capture()); - attributesConsumer.accept( - OpenTelemetryAssertions.assertThat(attributesArgumentCaptor.getValue())); + attributesConsumer.accept(assertThat(attributesArgumentCaptor.getValue())); } private static Stream processUrlArguments() { diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy deleted file mode 100644 index d91d38ca6ba9..000000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import org.springframework.batch.core.JobParameter -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.context.support.ClassPathXmlApplicationContext - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.api.trace.StatusCode.ERROR -import static java.util.Collections.emptyMap - -abstract class SpringBatchTest extends AgentInstrumentationSpecification { - - abstract runJob(String jobName, Map params = emptyMap()) - - def "should trace tasklet job+step"() { - when: - runJob("taskletJob") - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - name "BatchJob taskletJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - name "BatchJob taskletJob.step" - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name "BatchJob taskletJob.step.Tasklet" - kind INTERNAL - childOf span(1) - attributes {} - } - } - } - } - - def "should handle exception in tasklet job+step"() { - when: - runJob("taskletJob", ["fail": new JobParameter(1)]) - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - name "BatchJob taskletJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - name "BatchJob taskletJob.step" - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name "BatchJob taskletJob.step.Tasklet" - kind INTERNAL - childOf span(1) - status ERROR - errorEvent IllegalStateException, "fail" - attributes {} - } - } - } - } - - def "should trace chunked items job"() { - when: - runJob("itemsAndTaskletJob") - - then: - assertTraces(1) { - trace(0, 7) { - span(0) { - name "BatchJob itemsAndTaskletJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - name "BatchJob itemsAndTaskletJob.itemStep" - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - attributes {} - } - span(3) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - attributes {} - } - span(4) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - attributes {} - } - span(5) { - name "BatchJob itemsAndTaskletJob.taskletStep" - kind INTERNAL - childOf span(0) - attributes {} - } - span(6) { - name "BatchJob itemsAndTaskletJob.taskletStep.Tasklet" - kind INTERNAL - childOf span(5) - attributes {} - } - } - } - } - - def "should trace flow job"() { - when: - runJob("flowJob") - - then: - assertTraces(1) { - trace(0, 5) { - span(0) { - name "BatchJob flowJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - name "BatchJob flowJob.flowStep1" - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name "BatchJob flowJob.flowStep1.Tasklet" - kind INTERNAL - childOf span(1) - attributes {} - } - span(3) { - name "BatchJob flowJob.flowStep2" - kind INTERNAL - childOf span(0) - attributes {} - } - span(4) { - name "BatchJob flowJob.flowStep2.Tasklet" - kind INTERNAL - childOf span(3) - attributes {} - } - } - } - } - - def "should trace split flow job"() { - when: - runJob("splitJob") - - then: - assertTraces(1) { - trace(0, 5) { - span(0) { - name "BatchJob splitJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - name ~/BatchJob splitJob\.splitFlowStep[12]/ - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name ~/BatchJob splitJob\.splitFlowStep[12]\.Tasklet/ - kind INTERNAL - childOf span(1) - attributes {} - } - span(3) { - name ~/BatchJob splitJob\.splitFlowStep[12]/ - kind INTERNAL - childOf span(0) - attributes {} - } - span(4) { - name ~/BatchJob splitJob\.splitFlowStep[12]\.Tasklet/ - kind INTERNAL - childOf span(3) - attributes {} - } - } - } - } - - def "should trace job with decision"() { - when: - runJob("decisionJob") - - then: - assertTraces(1) { - trace(0, 5) { - span(0) { - name "BatchJob decisionJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - name "BatchJob decisionJob.decisionStepStart" - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name "BatchJob decisionJob.decisionStepStart.Tasklet" - kind INTERNAL - childOf span(1) - attributes {} - } - span(3) { - name "BatchJob decisionJob.decisionStepLeft" - kind INTERNAL - childOf span(0) - attributes {} - } - span(4) { - name "BatchJob decisionJob.decisionStepLeft.Tasklet" - kind INTERNAL - childOf span(3) - attributes {} - } - } - } - } - - def "should trace partitioned job"() { - when: - runJob("partitionedJob") - - then: - assertTraces(1) { - trace(0, 8) { - span(0) { - name "BatchJob partitionedJob" - kind INTERNAL - attributes { - "job.system" "spring_batch" - } - } - span(1) { - def stepName = hasPartitionManagerStep() ? "partitionManagerStep" : "partitionWorkerStep" - name "BatchJob partitionedJob.$stepName" - kind INTERNAL - childOf span(0) - attributes {} - } - span(2) { - name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01]/ - kind INTERNAL - childOf span(1) - attributes {} - } - span(3) { - name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ - kind INTERNAL - childOf span(2) - attributes {} - } - span(4) { - name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ - kind INTERNAL - childOf span(2) - attributes {} - } - span(5) { - name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01]/ - kind INTERNAL - childOf span(1) - attributes {} - } - span(6) { - name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ - kind INTERNAL - childOf span(5) - attributes {} - } - span(7) { - name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ - kind INTERNAL - childOf span(5) - attributes {} - } - } - } - } - - protected boolean hasPartitionManagerStep() { - true - } -} - -class JavaConfigBatchJobTest extends SpringBatchTest implements ApplicationConfigTrait { - @Override - ConfigurableApplicationContext createApplicationContext() { - new AnnotationConfigApplicationContext(SpringBatchApplication) - } -} - -class XmlConfigBatchJobTest extends SpringBatchTest implements ApplicationConfigTrait { - @Override - ConfigurableApplicationContext createApplicationContext() { - new ClassPathXmlApplicationContext("spring-batch.xml") - } -} - -class JsrConfigBatchJobTest extends SpringBatchTest implements JavaxBatchConfigTrait { - protected boolean hasPartitionManagerStep() { - false - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/JavaConfigBatchJobTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/JavaConfigBatchJobTest.java new file mode 100644 index 000000000000..1401a587fa7c --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/JavaConfigBatchJobTest.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.basic; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.SpringBatchApplication; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +class JavaConfigBatchJobTest extends SpringBatchTest { + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner( + () -> new AnnotationConfigApplicationContext(SpringBatchApplication.class)); + + public JavaConfigBatchJobTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/JsrConfigBatchJobTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/JsrConfigBatchJobTest.java new file mode 100644 index 000000000000..c2cc24315545 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/JsrConfigBatchJobTest.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.basic; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JavaxBatchConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JsrConfigBatchJobTest extends SpringBatchTest { + @Override + protected boolean hasPartitionManagerStep() { + return false; + } + + @RegisterExtension static final JavaxBatchConfigRunner runner = new JavaxBatchConfigRunner(); + + public JsrConfigBatchJobTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/SpringBatchTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/SpringBatchTest.java new file mode 100644 index 000000000000..1465fda06559 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/SpringBatchTest.java @@ -0,0 +1,279 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.basic; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static java.util.Collections.singletonMap; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JobRunner; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import io.opentelemetry.sdk.trace.data.StatusData; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.batch.core.JobParameter; + +abstract class SpringBatchTest { + + private final JobRunner runner; + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + public SpringBatchTest(JobRunner runner) { + this.runner = runner; + } + + @Test + void shouldTraceTaskletJobStep() { + runner.runJob("taskletJob"); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob taskletJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.hasName("BatchJob taskletJob.step") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob taskletJob.step.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(1)))); + } + + @Test + void shouldHandleExceptionInTaskletJobStep() { + runner.runJob("taskletJob", singletonMap("fail", new JobParameter(1L))); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob taskletJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.hasName("BatchJob taskletJob.step") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob taskletJob.step.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)) + .hasStatus(StatusData.error()) + .hasTotalAttributeCount(0) + .hasException(new IllegalStateException("fail")))); + } + + @Test + void shouldTraceChunkedItemsJob() { + runner.runJob("itemsAndTaskletJob"); + + testing.waitAndAssertTraces( + trace -> { + Consumer chunk = + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(1)); + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob itemsAndTaskletJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + chunk, + chunk, + chunk, + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(5))); + }); + } + + @Test + void shouldTraceFlowJob() { + runner.runJob("flowJob"); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob flowJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.hasName("BatchJob flowJob.flowStep1") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob flowJob.flowStep1.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(1)), + span -> + span.hasName("BatchJob flowJob.flowStep2") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob flowJob.flowStep2.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(3)))); + } + + @Test + void shouldTraceSplitFlowJob() { + runner.runJob("splitJob"); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob splitJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches("BatchJob splitJob.splitFlowStep[12]")) + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches("BatchJob splitJob.splitFlowStep[12].Tasklet")) + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(1)), + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches("BatchJob splitJob.splitFlowStep[12]")) + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches("BatchJob splitJob.splitFlowStep[12].Tasklet")) + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(3)))); + } + + @Test + void shouldTraceJobWithDecision() { + runner.runJob("decisionJob"); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob decisionJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.hasName("BatchJob decisionJob.decisionStepStart") + .hasKind(SpanKind.INTERNAL) + .hasTotalAttributeCount(0) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob decisionJob.decisionStepStart.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)), + span -> + span.hasName("BatchJob decisionJob.decisionStepLeft") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob decisionJob.decisionStepLeft.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(3)))); + } + + @Test + void shouldTracePartitionedJob() { + runner.runJob("partitionedJob"); + + testing.waitAndAssertTraces( + trace -> { + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob partitionedJob") + .hasKind(SpanKind.INTERNAL) + .hasAttribute(AttributeKey.stringKey("job.system"), "spring_batch"), + span -> + span.hasName( + hasPartitionManagerStep() + ? "BatchJob partitionedJob.partitionManagerStep" + : "BatchJob partitionedJob.partitionWorkerStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches( + "BatchJob partitionedJob.partitionWorkerStep:partition[01]")) + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)), + span -> partitionChunk(trace, span, 2), + span -> partitionChunk(trace, span, 2), + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches( + "BatchJob partitionedJob.partitionWorkerStep:partition[01]")) + .hasParent(trace.getSpan(1)), + span -> partitionChunk(trace, span, 5), + span -> partitionChunk(trace, span, 5)); + }); + } + + private static void partitionChunk(TraceAssert trace, SpanDataAssert span, int index) { + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches("BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk")) + .hasParent(trace.getSpan(index)); + } + + protected boolean hasPartitionManagerStep() { + return true; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/XmlConfigBatchJobTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/XmlConfigBatchJobTest.java new file mode 100644 index 000000000000..19a395594619 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/basic/XmlConfigBatchJobTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.basic; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +class XmlConfigBatchJobTest extends SpringBatchTest { + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner(() -> new ClassPathXmlApplicationContext("spring-batch.xml")); + + public XmlConfigBatchJobTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventChunkListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventChunkListener.java new file mode 100644 index 000000000000..d0c421d968ae --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventChunkListener.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import io.opentelemetry.api.trace.Span; +import javax.batch.api.chunk.listener.ChunkListener; + +public class CustomEventChunkListener implements ChunkListener { + @Override + public void beforeChunk() { + Span.current().addEvent("chunk.before"); + } + + @Override + public void onError(Exception e) { + Span.current().addEvent("chunk.error"); + } + + @Override + public void afterChunk() { + Span.current().addEvent("chunk.after"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemProcessListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemProcessListener.java new file mode 100644 index 000000000000..95a37221ae21 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemProcessListener.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import io.opentelemetry.api.trace.Span; +import javax.batch.api.chunk.listener.ItemProcessListener; + +class CustomEventItemProcessListener implements ItemProcessListener { + @Override + public void beforeProcess(Object o) { + Span.current().addEvent("item.process.before"); + } + + @Override + public void afterProcess(Object o, Object o1) { + Span.current().addEvent("item.process.after"); + } + + @Override + public void onProcessError(Object o, Exception e) { + Span.current().addEvent("item.process.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemReadListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemReadListener.java new file mode 100644 index 000000000000..72d64e28f308 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemReadListener.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import io.opentelemetry.api.trace.Span; +import javax.batch.api.chunk.listener.ItemReadListener; + +class CustomEventItemReadListener implements ItemReadListener { + @Override + public void beforeRead() { + Span.current().addEvent("item.read.before"); + } + + @Override + public void afterRead(Object o) { + Span.current().addEvent("item.read.after"); + } + + @Override + public void onReadError(Exception e) { + Span.current().addEvent("item.read.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemWriteListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemWriteListener.java new file mode 100644 index 000000000000..063bb271cbfe --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventItemWriteListener.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import io.opentelemetry.api.trace.Span; +import java.util.List; +import javax.batch.api.chunk.listener.ItemWriteListener; + +class CustomEventItemWriteListener implements ItemWriteListener { + @Override + public void beforeWrite(List list) { + Span.current().addEvent("item.write.before"); + } + + @Override + public void afterWrite(List list) { + Span.current().addEvent("item.write.after"); + } + + @Override + public void onWriteError(List list, Exception e) { + Span.current().addEvent("item.write.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventJobListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventJobListener.java new file mode 100644 index 000000000000..0999cd12864c --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventJobListener.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import io.opentelemetry.api.trace.Span; +import javax.batch.api.listener.JobListener; + +class CustomEventJobListener implements JobListener { + @Override + public void beforeJob() { + Span.current().addEvent("job.before"); + } + + @Override + public void afterJob() { + Span.current().addEvent("job.after"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventStepListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventStepListener.java new file mode 100644 index 000000000000..e14081ada7aa --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/CustomEventStepListener.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import io.opentelemetry.api.trace.Span; +import javax.batch.api.listener.StepListener; + +class CustomEventStepListener implements StepListener { + @Override + public void beforeStep() { + Span.current().addEvent("step.before"); + } + + @Override + public void afterStep() { + Span.current().addEvent("step.after"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/SingleItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/SingleItemReader.java new file mode 100644 index 000000000000..d7f334cd77d9 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/SingleItemReader.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicReference; +import javax.batch.api.chunk.ItemReader; + +class SingleItemReader implements ItemReader { + @Override + public void open(Serializable serializable) {} + + @Override + public void close() {} + + @Override + public Object readItem() { + return item.getAndSet(null); + } + + @Override + public Serializable checkpointInfo() { + return null; + } + + public final AtomicReference getItem() { + return item; + } + + private final AtomicReference item = new AtomicReference("42"); +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestBatchlet.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestBatchlet.java new file mode 100644 index 000000000000..813b0bbcf8d0 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestBatchlet.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import javax.batch.api.BatchProperty; +import javax.batch.api.Batchlet; +import javax.inject.Inject; + +class TestBatchlet implements Batchlet { + @Inject + @BatchProperty(name = "fail") + private String fail; + + @Override + public String process() { + if (fail != null && Integer.valueOf(fail) == 1) { + throw new IllegalStateException("fail"); + } + + return "FINISHED"; + } + + @Override + public void stop() {} + + public String getFail() { + return fail; + } + + public void setFail(String fail) { + this.fail = fail; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestDecider.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestDecider.java new file mode 100644 index 000000000000..71ff22671f6b --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestDecider.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import javax.batch.api.Decider; +import javax.batch.runtime.StepExecution; + +class TestDecider implements Decider { + @Override + public String decide(StepExecution[] stepExecutions) { + return "LEFT"; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemProcessor.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemProcessor.java new file mode 100644 index 000000000000..3814b823ff02 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemProcessor.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import javax.batch.api.chunk.ItemProcessor; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; + +class TestItemProcessor implements ItemProcessor { + @Override + public Object processItem(Object item) { + return Integer.parseInt(DefaultGroovyMethods.asType(item, String.class)); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemReader.java new file mode 100644 index 000000000000..41fb17e5e298 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemReader.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import javax.batch.api.chunk.ItemReader; + +class TestItemReader implements ItemReader { + + private final List items = + IntStream.range(0, 13).mapToObj(String::valueOf).collect(Collectors.toList()); + private Iterator itemsIt; + + @Override + public void open(Serializable serializable) { + itemsIt = items.iterator(); + } + + @Override + public void close() { + itemsIt = null; + } + + @Override + public Object readItem() { + if (itemsIt == null) { + return null; + } + + return itemsIt.hasNext() ? itemsIt.next() : null; + } + + @Override + public Serializable checkpointInfo() { + return null; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemWriter.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemWriter.java new file mode 100644 index 000000000000..c14aeb871924 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestItemWriter.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.batch.api.chunk.ItemWriter; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; + +class TestItemWriter implements ItemWriter { + private final List items = new ArrayList<>(); + + @Override + public void open(Serializable checkpoint) {} + + @Override + public void close() {} + + @Override + public void writeItems(List items) { + for (Object item : items) { + this.items.add(DefaultGroovyMethods.asType(item, Integer.class)); + } + } + + @Override + public Serializable checkpointInfo() { + return null; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestPartitionedItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestPartitionedItemReader.java new file mode 100644 index 000000000000..d3b0820e2f48 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/jsr/TestPartitionedItemReader.java @@ -0,0 +1,80 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.jsr; + +import java.io.Serializable; +import javax.batch.api.BatchProperty; +import javax.batch.api.chunk.ItemReader; +import javax.inject.Inject; + +class TestPartitionedItemReader implements ItemReader { + + @Inject + @BatchProperty(name = "start") + private String startStr; + + @Inject + @BatchProperty(name = "end") + private String endStr; + + private int start; + private int end; + + @Override + public void open(Serializable checkpoint) { + start = Integer.parseInt(startStr); + end = Integer.parseInt(endStr); + } + + @Override + public void close() {} + + @Override + public Object readItem() { + if (start >= end) { + return null; + } + + return String.valueOf(start++); + } + + @Override + public Serializable checkpointInfo() { + return null; + } + + public String getStartStr() { + return startStr; + } + + public void setStartStr(String startStr) { + this.startStr = startStr; + } + + public String getEndStr() { + return endStr; + } + + public void setEndStr(String endStr) { + this.endStr = endStr; + } + + public int getStart() { + return start; + } + + public void setStart(int start) { + this.start = start; + } + + public int getEnd() { + return end; + } + + public void setEnd(int end) { + this.end = end; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/ApplicationConfigRunner.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/ApplicationConfigRunner.java new file mode 100644 index 000000000000..79ecbc0e428f --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/ApplicationConfigRunner.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner; + +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameter; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.context.ConfigurableApplicationContext; + +public class ApplicationConfigRunner implements BeforeEachCallback, AfterEachCallback, JobRunner { + private final Supplier applicationContextFactory; + private final BiConsumer jobPostProcessor; + static JobLauncher jobLauncher; + private ConfigurableApplicationContext applicationContext; + + public ApplicationConfigRunner( + Supplier applicationContextFactory) { + this(applicationContextFactory, (jobName, job) -> {}); + } + + public ApplicationConfigRunner( + Supplier applicationContextFactory, + BiConsumer jobPostProcessor) { + this.applicationContextFactory = applicationContextFactory; + this.jobPostProcessor = jobPostProcessor; + } + + @Override + public void beforeEach(ExtensionContext context) { + applicationContext = applicationContextFactory.get(); + applicationContext.start(); + + jobLauncher = applicationContext.getBean(JobLauncher.class); + } + + @Override + public void afterEach(ExtensionContext context) { + applicationContext.stop(); + applicationContext.close(); + } + + @Override + public void runJob(String jobName, Map params) { + Job job = applicationContext.getBean(jobName, Job.class); + jobPostProcessor.accept(jobName, job); + try { + jobLauncher.run(job, new JobParameters(params)); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/JavaxBatchConfigRunner.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/JavaxBatchConfigRunner.java new file mode 100644 index 000000000000..e92886d996ff --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/JavaxBatchConfigRunner.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner; + +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; +import javax.batch.operations.JobOperator; +import javax.batch.runtime.BatchRuntime; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.batch.core.JobParameter; + +public class JavaxBatchConfigRunner implements BeforeEachCallback, JobRunner { + static JobOperator jobOperator; + static AtomicInteger counter = new AtomicInteger(); + + @Override + public void beforeEach(ExtensionContext context) { + jobOperator = BatchRuntime.getJobOperator(); + } + + @Override + public void runJob(String jobName, Map params) { + Properties jobParams = new Properties(); + params.forEach((k, v) -> jobParams.setProperty(k, v.getValue().toString())); + // each job instance with the same name needs to be unique + jobParams.setProperty("uniqueJobIdCounter", String.valueOf(counter.getAndIncrement())); + jobOperator.start(jobName, jobParams); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/JobRunner.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/JobRunner.java new file mode 100644 index 000000000000..7368a7491083 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/JobRunner.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner; + +import static java.util.Collections.emptyMap; + +import java.util.Map; +import org.springframework.batch.core.JobParameter; + +public interface JobRunner { + void runJob(String jobName, Map params); + + default void runJob(String jobName) { + runJob(jobName, emptyMap()); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/SpringBatchApplication.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/SpringBatchApplication.java new file mode 100644 index 000000000000..a1d790e81b5b --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/runner/SpringBatchApplication.java @@ -0,0 +1,274 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.CustomEventChunkListener; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.CustomEventItemProcessListener; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.CustomEventItemReadListener; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.CustomEventItemWriteListener; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.CustomEventJobListener; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.CustomEventStepListener; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.SingleItemReader; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestDecider; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestItemProcessor; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestItemReader; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestItemWriter; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestPartitionedItemReader; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestPartitioner; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestSyncItemReader; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch.TestTasklet; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.job.builder.FlowBuilder; +import org.springframework.batch.core.job.flow.Flow; +import org.springframework.batch.core.job.flow.support.SimpleFlow; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.support.SimpleJobLauncher; +import org.springframework.batch.core.partition.support.Partitioner; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableBatchProcessing +public class SpringBatchApplication { + + @Autowired JobBuilderFactory jobs; + @Autowired StepBuilderFactory steps; + @Autowired JobRepository jobRepository; + + @Bean + AsyncTaskExecutor asyncTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); + executor.setMaxPoolSize(10); + return executor; + } + + @Bean + JobLauncher jobLauncher() { + SimpleJobLauncher launcher = new SimpleJobLauncher(); + launcher.setJobRepository(jobRepository); + launcher.setTaskExecutor(asyncTaskExecutor()); + return launcher; + } + + // common + @Bean + ItemReader itemReader() { + return new TestItemReader(); + } + + @Bean + ItemProcessor itemProcessor() { + return new TestItemProcessor(); + } + + @Bean + ItemWriter itemWriter() { + return new TestItemWriter(); + } + + // simple tasklet job + @Bean + Job taskletJob() { + return jobs.get("taskletJob").start(step()).build(); + } + + @Bean + Step step() { + return steps.get("step").tasklet(new TestTasklet()).build(); + } + + // 2-step tasklet + chunked items job + @Bean + Job itemsAndTaskletJob() { + return jobs.get("itemsAndTaskletJob").start(itemStep()).next(taskletStep()).build(); + } + + @Bean + Step taskletStep() { + return steps.get("taskletStep").tasklet(new TestTasklet()).build(); + } + + @Bean + Step itemStep() { + return steps + .get("itemStep") + .chunk(5) + .reader(itemReader()) + .processor(itemProcessor()) + .writer(itemWriter()) + .build(); + } + + // parallel items job + @Bean + Job parallelItemsJob() { + return jobs.get("parallelItemsJob").start(parallelItemsStep()).build(); + } + + @Bean + Step parallelItemsStep() { + return steps + .get("parallelItemsStep") + .chunk(2) + .reader(new TestSyncItemReader(5)) + .processor(itemProcessor()) + .writer(itemWriter()) + .taskExecutor(asyncTaskExecutor()) + .throttleLimit(2) + .build(); + } + + // job using a flow + @Bean + Job flowJob() { + return jobs.get("flowJob").start(flow()).build().build(); + } + + @Bean + Flow flow() { + return new FlowBuilder("flow").start(flowStep1()).on("*").to(flowStep2()).build(); + } + + @Bean + Step flowStep1() { + return steps.get("flowStep1").tasklet(new TestTasklet()).build(); + } + + @Bean + Step flowStep2() { + return steps.get("flowStep2").tasklet(new TestTasklet()).build(); + } + + // split job + @Bean + Job splitJob() { + return jobs.get("splitJob") + .start(splitFlowStep1()) + .split(asyncTaskExecutor()) + .add(splitFlow2()) + .build() + .build(); + } + + @Bean + Step splitFlowStep1() { + return steps.get("splitFlowStep1").tasklet(new TestTasklet()).build(); + } + + @Bean + Flow splitFlow2() { + return new FlowBuilder("splitFlow2").start(splitFlowStep2()).build(); + } + + @Bean + Step splitFlowStep2() { + return steps.get("splitFlowStep2").tasklet(new TestTasklet()).build(); + } + + // job with decisions + @Bean + Job decisionJob() { + return jobs.get("decisionJob") + .start(decisionStepStart()) + .next(new TestDecider()) + .on("LEFT") + .to(decisionStepLeft()) + .on("RIGHT") + .to(decisionStepRight()) + .end() + .build(); + } + + @Bean + Step decisionStepStart() { + return steps.get("decisionStepStart").tasklet(new TestTasklet()).build(); + } + + @Bean + Step decisionStepLeft() { + return steps.get("decisionStepLeft").tasklet(new TestTasklet()).build(); + } + + @Bean + Step decisionStepRight() { + return steps.get("decisionStepRight").tasklet(new TestTasklet()).build(); + } + + // partitioned job + @Bean + Job partitionedJob() { + return jobs.get("partitionedJob").start(partitionManagerStep()).build(); + } + + @Bean + Step partitionManagerStep() { + return steps + .get("partitionManagerStep") + .partitioner("partitionWorkerStep", partitioner()) + .step(partitionWorkerStep()) + .gridSize(2) + .taskExecutor(asyncTaskExecutor()) + .build(); + } + + @Bean + Partitioner partitioner() { + return new TestPartitioner(); + } + + @Bean + Step partitionWorkerStep() { + return steps + .get("partitionWorkerStep") + .chunk(5) + .reader(partitionedItemReader()) + .processor(itemProcessor()) + .writer(itemWriter()) + .build(); + } + + @Bean + ItemReader partitionedItemReader() { + return new TestPartitionedItemReader(); + } + + // custom span events items job + @Bean + Job customSpanEventsItemsJob() { + return jobs.get("customSpanEventsItemsJob") + .start(customSpanEventsItemStep()) + .listener(new CustomEventJobListener()) + .build(); + } + + @Bean + Step customSpanEventsItemStep() { + return steps + .get("customSpanEventsItemStep") + .chunk(5) + .reader(new SingleItemReader()) + .processor(itemProcessor()) + .writer(itemWriter()) + .listener(new CustomEventStepListener()) + .listener(new CustomEventChunkListener()) + .listener(new CustomEventItemReadListener()) + .listener(new CustomEventItemProcessListener()) + .listener(new CustomEventItemWriteListener()) + .build(); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventChunkListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventChunkListener.java new file mode 100644 index 000000000000..1b5dd8a15efa --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventChunkListener.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import io.opentelemetry.api.trace.Span; +import org.springframework.batch.core.ChunkListener; +import org.springframework.batch.core.scope.context.ChunkContext; + +public class CustomEventChunkListener implements ChunkListener { + @Override + public void beforeChunk(ChunkContext context) { + Span.current().addEvent("chunk.before"); + } + + @Override + public void afterChunk(ChunkContext context) { + Span.current().addEvent("chunk.after"); + } + + @Override + public void afterChunkError(ChunkContext context) { + Span.current().addEvent("chunk.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemProcessListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemProcessListener.java new file mode 100644 index 000000000000..fa37fe866810 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemProcessListener.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import io.opentelemetry.api.trace.Span; +import org.springframework.batch.core.ItemProcessListener; + +public class CustomEventItemProcessListener implements ItemProcessListener { + @Override + public void beforeProcess(String item) { + Span.current().addEvent("item.process.before"); + } + + @Override + public void afterProcess(String item, Integer result) { + Span.current().addEvent("item.process.after"); + } + + @Override + public void onProcessError(String item, Exception e) { + Span.current().addEvent("item.process.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemReadListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemReadListener.java new file mode 100644 index 000000000000..9486112372d2 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemReadListener.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import io.opentelemetry.api.trace.Span; +import org.springframework.batch.core.ItemReadListener; + +public class CustomEventItemReadListener implements ItemReadListener { + @Override + public void beforeRead() { + Span.current().addEvent("item.read.before"); + } + + @Override + public void afterRead(String item) { + Span.current().addEvent("item.read.after"); + } + + @Override + public void onReadError(Exception ex) { + Span.current().addEvent("item.read.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemWriteListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemWriteListener.java new file mode 100644 index 000000000000..7fee43ce01a8 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventItemWriteListener.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import io.opentelemetry.api.trace.Span; +import java.util.List; +import org.springframework.batch.core.ItemWriteListener; + +public class CustomEventItemWriteListener implements ItemWriteListener { + @Override + public void beforeWrite(List items) { + Span.current().addEvent("item.write.before"); + } + + @Override + public void afterWrite(List items) { + Span.current().addEvent("item.write.after"); + } + + @Override + public void onWriteError(Exception exception, List items) { + Span.current().addEvent("item.write.error"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventJobListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventJobListener.java new file mode 100644 index 000000000000..2ccd465b6e6a --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventJobListener.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import io.opentelemetry.api.trace.Span; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobExecutionListener; + +public class CustomEventJobListener implements JobExecutionListener { + @Override + public void beforeJob(JobExecution jobExecution) { + Span.current().addEvent("job.before"); + } + + @Override + public void afterJob(JobExecution jobExecution) { + Span.current().addEvent("job.after"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventStepListener.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventStepListener.java new file mode 100644 index 000000000000..5fc343556673 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/CustomEventStepListener.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import io.opentelemetry.api.trace.Span; +import org.springframework.batch.core.ExitStatus; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.StepExecutionListener; + +public class CustomEventStepListener implements StepExecutionListener { + @Override + public void beforeStep(StepExecution stepExecution) { + Span.current().addEvent("step.before"); + } + + @Override + public ExitStatus afterStep(StepExecution stepExecution) { + Span.current().addEvent("step.after"); + return null; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/SingleItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/SingleItemReader.java new file mode 100644 index 000000000000..6e4cd14b7ff1 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/SingleItemReader.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import java.util.concurrent.atomic.AtomicReference; +import org.springframework.batch.item.ItemReader; + +public class SingleItemReader implements ItemReader { + final AtomicReference item = new AtomicReference<>("42"); + + @Override + public String read() { + return item.getAndSet(null); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestDecider.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestDecider.java new file mode 100644 index 000000000000..4911d3b794e0 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestDecider.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.job.flow.FlowExecutionStatus; +import org.springframework.batch.core.job.flow.JobExecutionDecider; + +public class TestDecider implements JobExecutionDecider { + @Override + public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) { + return new FlowExecutionStatus("LEFT"); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemProcessor.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemProcessor.java new file mode 100644 index 000000000000..c4aa9cbf9b13 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemProcessor.java @@ -0,0 +1,15 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import org.springframework.batch.item.ItemProcessor; + +public class TestItemProcessor implements ItemProcessor { + @Override + public Integer process(String item) { + return Integer.parseInt(item); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemReader.java new file mode 100644 index 000000000000..c66a1ff3243c --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemReader.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.springframework.batch.item.support.ListItemReader; + +public class TestItemReader extends ListItemReader { + public TestItemReader() { + super(IntStream.range(0, 13).mapToObj(String::valueOf).collect(Collectors.toList())); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemWriter.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemWriter.java new file mode 100644 index 000000000000..d660c8079ea9 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestItemWriter.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import static java.util.Collections.synchronizedList; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.batch.item.ItemWriter; + +public class TestItemWriter implements ItemWriter { + final List items = synchronizedList(new ArrayList<>()); + + @Override + public void write(List items) { + this.items.addAll(items); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestPartitionedItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestPartitionedItemReader.java new file mode 100644 index 000000000000..18b1181a83e6 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestPartitionedItemReader.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemStream; + +public class TestPartitionedItemReader implements ItemReader, ItemStream { + ThreadLocal start = new ThreadLocal<>(); + ThreadLocal end = new ThreadLocal<>(); + + @Override + public String read() { + if (start.get() >= end.get()) { + return null; + } + Integer value = start.get(); + start.set(value + 1); + return String.valueOf(value); + } + + @Override + public void open(ExecutionContext executionContext) { + start.set(executionContext.getInt("start")); + end.set(executionContext.getInt("end")); + } + + @Override + public void update(ExecutionContext executionContext) {} + + @Override + public void close() {} +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestPartitioner.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestPartitioner.java new file mode 100644 index 000000000000..9f46037c9840 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestPartitioner.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import com.google.common.collect.ImmutableMap; +import java.util.HashMap; +import java.util.Map; +import org.springframework.batch.core.partition.support.Partitioner; +import org.springframework.batch.item.ExecutionContext; + +public class TestPartitioner implements Partitioner { + @Override + public Map partition(int gridSize) { + Map map = new HashMap<>(); + map.put("partition0", new ExecutionContext(ImmutableMap.of("start", 0, "end", 8))); + map.put("partition1", new ExecutionContext(ImmutableMap.of("start", 8, "end", 13))); + return map; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestSyncItemReader.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestSyncItemReader.java new file mode 100644 index 000000000000..62b673ef2ea9 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestSyncItemReader.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import java.util.Iterator; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.springframework.batch.item.ItemReader; + +public class TestSyncItemReader implements ItemReader { + private final Iterator items; + + public TestSyncItemReader(int max) { + items = + IntStream.range(0, max).mapToObj(String::valueOf).collect(Collectors.toList()).iterator(); + } + + @Override + public synchronized String read() { + if (items.hasNext()) { + return items.next(); + } + return null; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestTasklet.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestTasklet.java new file mode 100644 index 000000000000..e5cbe65f47f6 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/springbatch/TestTasklet.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.springbatch; + +import java.util.Objects; +import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.scope.context.ChunkContext; +import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.repeat.RepeatStatus; + +public class TestTasklet implements Tasklet { + @Override + public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) { + if (Objects.equals( + chunkContext.getStepContext().getStepExecution().getJobParameters().getLong("fail"), 1L)) { + throw new IllegalStateException("fail"); + } + return RepeatStatus.FINISHED; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/customSpanEventsItemsJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/customSpanEventsItemsJob.xml index feeff7e20a6d..0e3df085f3ed 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/customSpanEventsItemsJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/customSpanEventsItemsJob.xml @@ -1,20 +1,23 @@ - + - + - - - - - + + + + + - - - + + + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/decisionJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/decisionJob.xml index 3f2a98def83d..d6d972c159a8 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/decisionJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/decisionJob.xml @@ -1,16 +1,19 @@ - + - + - + - + - + - \ No newline at end of file + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/flowJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/flowJob.xml index a43b024d91d9..c6d647c9062e 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/flowJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/flowJob.xml @@ -1,11 +1,14 @@ - + - + - + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/itemsAndTaskletJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/itemsAndTaskletJob.xml index 7d2a890e8442..d2fc4565a4d4 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/itemsAndTaskletJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/itemsAndTaskletJob.xml @@ -1,14 +1,17 @@ - + - - - + + + - + - \ No newline at end of file + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/partitionedJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/partitionedJob.xml index 330f99c2c68e..42b4f709ec04 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/partitionedJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/partitionedJob.xml @@ -1,15 +1,18 @@ - + - + - - + + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/splitJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/splitJob.xml index a9f8356f46d5..8a274af8949c 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/splitJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/splitJob.xml @@ -1,14 +1,17 @@ - + - + - + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/taskletJob.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/taskletJob.xml index fe2f62430559..13043c08cdd3 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/taskletJob.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/META-INF/batch-jobs/taskletJob.xml @@ -1,10 +1,13 @@ - + - + - \ No newline at end of file + diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/spring-batch.xml b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/spring-batch.xml index e6b8ca3c78bb..0e9cf65992d7 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/spring-batch.xml +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/resources/spring-batch.xml @@ -90,31 +90,31 @@ writer="itemWriter"/> - - + + - + - + - + - + - + - + @@ -122,17 +122,17 @@ - + - - + + - - - - - \ No newline at end of file + + + + + diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/SpringResourceProviderTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/SpringResourceProviderTest.java index 614f33b37273..b2c4bc3b521c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/SpringResourceProviderTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/SpringResourceProviderTest.java @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtlpExporterProperties; @@ -13,7 +15,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.semconv.ServiceAttributes; import java.util.Collections; import java.util.Properties; @@ -70,7 +71,7 @@ private static AttributesAssert assertResourceAttributes(AssertableApplicationCo new PropagationProperties(), DefaultConfigProperties.createFromMap(Collections.emptyMap())); - return OpenTelemetryAssertions.assertThat( + return assertThat( context .getBean(SpringResourceProvider.class) .createResource(configProperties) diff --git a/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/gateway/v2_0/GatewayInstrumentationModule.java b/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/gateway/v2_0/GatewayInstrumentationModule.java index c5b719af8f14..a47ca107b88f 100644 --- a/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/gateway/v2_0/GatewayInstrumentationModule.java +++ b/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/gateway/v2_0/GatewayInstrumentationModule.java @@ -10,10 +10,12 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; @AutoService(InstrumentationModule.class) -public class GatewayInstrumentationModule extends InstrumentationModule { +public class GatewayInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public GatewayInstrumentationModule() { super("spring-cloud-gateway"); @@ -24,6 +26,12 @@ public List typeInstrumentations() { return asList(new HandlerAdapterInstrumentation()); } + @Override + public String getModuleGroup() { + // relies on netty + return "netty"; + } + @Override public int order() { // Later than Spring Webflux. diff --git a/instrumentation/spring/spring-data/spring-data-3.0/testing/src/test/java/SpringJpaTest.java b/instrumentation/spring/spring-data/spring-data-1.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/data/v1_8/SpringJpaTest.java similarity index 61% rename from instrumentation/spring/spring-data/spring-data-3.0/testing/src/test/java/SpringJpaTest.java rename to instrumentation/spring/spring-data/spring-data-1.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/data/v1_8/SpringJpaTest.java index a088c8589b6d..d662e52eb3d2 100644 --- a/instrumentation/spring/spring-data/spring-data-3.0/testing/src/test/java/SpringJpaTest.java +++ b/instrumentation/spring/spring-data/spring-data-1.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/data/v1_8/SpringJpaTest.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.javaagent.instrumentation.spring.data.v1_8; + +import io.opentelemetry.javaagent.instrumentation.spring.data.AbstractSpringJpaTest; import java.util.List; import java.util.Optional; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -13,27 +16,27 @@ public class SpringJpaTest extends AbstractSpringJpaTest { @Override - JpaCustomer newCustomer(String firstName, String lastName) { + protected JpaCustomer newCustomer(String firstName, String lastName) { return new JpaCustomer(firstName, lastName); } @Override - Long id(JpaCustomer customer) { + protected Long id(JpaCustomer customer) { return customer.getId(); } @Override - void setFirstName(JpaCustomer customer, String firstName) { + protected void setFirstName(JpaCustomer customer, String firstName) { customer.setFirstName(firstName); } @Override - Class repositoryClass() { + protected Class repositoryClass() { return JpaCustomerRepository.class; } @Override - JpaCustomerRepository repository() { + protected JpaCustomerRepository repository() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JpaPersistenceConfig.class); JpaCustomerRepository repo = context.getBean(JpaCustomerRepository.class); @@ -45,17 +48,18 @@ JpaCustomerRepository repository() { } @Override - List findByLastName(JpaCustomerRepository repository, String lastName) { + protected List findByLastName(JpaCustomerRepository repository, String lastName) { return repository.findByLastName(lastName); } @Override - List findSpecialCustomers(JpaCustomerRepository repository) { + protected List findSpecialCustomers(JpaCustomerRepository repository) { return repository.findSpecialCustomers(); } @Override - Optional findOneByLastName(JpaCustomerRepository repository, String lastName) { + protected Optional findOneByLastName( + JpaCustomerRepository repository, String lastName) { return repository.findOneByLastName(lastName); } } diff --git a/instrumentation/spring/spring-data/spring-data-1.8/javaagent/src/test/java/SpringJpaTest.java b/instrumentation/spring/spring-data/spring-data-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/data/v3_0/SpringJpaTest.java similarity index 61% rename from instrumentation/spring/spring-data/spring-data-1.8/javaagent/src/test/java/SpringJpaTest.java rename to instrumentation/spring/spring-data/spring-data-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/data/v3_0/SpringJpaTest.java index a088c8589b6d..fe4fffa719e5 100644 --- a/instrumentation/spring/spring-data/spring-data-1.8/javaagent/src/test/java/SpringJpaTest.java +++ b/instrumentation/spring/spring-data/spring-data-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/data/v3_0/SpringJpaTest.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.javaagent.instrumentation.spring.data.v3_0; + +import io.opentelemetry.javaagent.instrumentation.spring.data.AbstractSpringJpaTest; import java.util.List; import java.util.Optional; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -13,27 +16,27 @@ public class SpringJpaTest extends AbstractSpringJpaTest { @Override - JpaCustomer newCustomer(String firstName, String lastName) { + protected JpaCustomer newCustomer(String firstName, String lastName) { return new JpaCustomer(firstName, lastName); } @Override - Long id(JpaCustomer customer) { + protected Long id(JpaCustomer customer) { return customer.getId(); } @Override - void setFirstName(JpaCustomer customer, String firstName) { + protected void setFirstName(JpaCustomer customer, String firstName) { customer.setFirstName(firstName); } @Override - Class repositoryClass() { + protected Class repositoryClass() { return JpaCustomerRepository.class; } @Override - JpaCustomerRepository repository() { + protected JpaCustomerRepository repository() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JpaPersistenceConfig.class); JpaCustomerRepository repo = context.getBean(JpaCustomerRepository.class); @@ -45,17 +48,18 @@ JpaCustomerRepository repository() { } @Override - List findByLastName(JpaCustomerRepository repository, String lastName) { + protected List findByLastName(JpaCustomerRepository repository, String lastName) { return repository.findByLastName(lastName); } @Override - List findSpecialCustomers(JpaCustomerRepository repository) { + protected List findSpecialCustomers(JpaCustomerRepository repository) { return repository.findSpecialCustomers(); } @Override - Optional findOneByLastName(JpaCustomerRepository repository, String lastName) { + protected Optional findOneByLastName( + JpaCustomerRepository repository, String lastName) { return repository.findOneByLastName(lastName); } } diff --git a/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/AbstractSpringJpaTest.java b/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java similarity index 96% rename from instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/AbstractSpringJpaTest.java rename to instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java index 670999905aa5..730fc05da074 100644 --- a/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/AbstractSpringJpaTest.java +++ b/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.javaagent.instrumentation.spring.data; + import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.assertj.core.api.Assertions.catchThrowableOfType; @@ -33,23 +35,23 @@ public abstract class AbstractSpringJpaTest< @RegisterExtension private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - abstract ENTITY newCustomer(String firstName, String lastName); + protected abstract ENTITY newCustomer(String firstName, String lastName); - abstract Long id(ENTITY customer); + protected abstract Long id(ENTITY customer); - abstract void setFirstName(ENTITY customer, String firstName); + protected abstract void setFirstName(ENTITY customer, String firstName); - abstract Class repositoryClass(); + protected abstract Class repositoryClass(); - abstract REPOSITORY repository(); + protected abstract REPOSITORY repository(); - abstract List findByLastName(REPOSITORY repository, String lastName); + protected abstract List findByLastName(REPOSITORY repository, String lastName); - abstract List findSpecialCustomers(REPOSITORY repository); + protected abstract List findSpecialCustomers(REPOSITORY repository); - abstract Optional findOneByLastName(REPOSITORY repository, String lastName); + protected abstract Optional findOneByLastName(REPOSITORY repository, String lastName); - void clearData() { + protected void clearData() { testing.clearData(); } diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/ComplexPropagationTest.groovy b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/ComplexPropagationTest.groovy deleted file mode 100644 index c5c32311b105..000000000000 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/ComplexPropagationTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class ComplexPropagationTest extends AbstractComplexPropagationTest implements AgentTestTrait { - @Override - Class additionalContextClass() { - null - } -} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringCloudStreamProducerTest.groovy b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringCloudStreamProducerTest.groovy deleted file mode 100644 index 07bf969f66a6..000000000000 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringCloudStreamProducerTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest implements AgentTestTrait { - @Override - Class additionalContextClass() { - null - } -} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringCloudStreamRabbitTest.groovy b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringCloudStreamRabbitTest.groovy deleted file mode 100644 index fa7447ea8b55..000000000000 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringCloudStreamRabbitTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest implements AgentTestTrait { - @Override - Class additionalContextClass() { - null - } -} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy deleted file mode 100644 index 33fc7906b3bf..000000000000 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes -import io.opentelemetry.semconv.NetworkAttributes - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.PRODUCER - -class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification implements WithRabbitProducerConsumerTrait { - def setupSpec() { - startRabbit() - } - - def cleanupSpec() { - stopRabbit() - } - - def "should cooperate with existing RabbitMQ instrumentation"() { - when: - runWithSpan("parent") { - producerContext.getBean("producer", Runnable).run() - } - - then: - assertTraces(2) { - trace(0, 7) { - span(0) { - name "parent" - attributes {} - } - span(1) { - name "producer" - childOf span(0) - attributes {} - } - span(2) { - // span created by rabbitmq instrumentation - name "exchange.declare" - childOf span(1) - kind CLIENT - attributes { - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq" - } - } - span(3) { - // span created by rabbitmq instrumentation - name "testTopic publish" - childOf span(1) - kind PRODUCER - attributes { - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq" - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "testTopic" - "$MessagingIncubatingAttributes.MESSAGING_OPERATION" "publish" - "$MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE" Long - "$MessagingIncubatingAttributes.MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY" String - } - } - // spring-cloud-stream-binder-rabbit listener puts all messages into a BlockingQueue immediately after receiving - // that's why the rabbitmq CONSUMER span will never have any child span (and propagate context, actually) - span(4) { - // span created by rabbitmq instrumentation - name ~/testTopic.anonymous.[-\w]+ process/ - childOf span(3) - kind CONSUMER - attributes { - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq" - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "testTopic" - "$MessagingIncubatingAttributes.MESSAGING_OPERATION" "process" - "$MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE" Long - "$MessagingIncubatingAttributes.MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY" String - } - } - // spring-integration will detect that spring-rabbit has already created a consumer span and back off - span(5) { - // span created by spring-rabbit instrumentation - name "testTopic process" - childOf span(3) - kind CONSUMER - attributes { - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq" - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "testTopic" - "$MessagingIncubatingAttributes.MESSAGING_OPERATION" "process" - "$MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID" String - "$MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE" Long - } - } - span(6) { - name "consumer" - childOf span(5) - attributes {} - } - } - - trace(1, 1) { - span(0) { - // span created by rabbitmq instrumentation - name "basic.ack" - kind CLIENT - attributes { - "$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null } - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq" - } - } - } - } - } -} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationTelemetryTest.groovy b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationTelemetryTest.groovy deleted file mode 100644 index 9adea629a504..000000000000 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationTelemetryTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest implements AgentTestTrait { - @Override - Class additionalContextClass() { - null - } -} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/ComplexPropagationTest.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/ComplexPropagationTest.java new file mode 100644 index 000000000000..27ca45e38815 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/ComplexPropagationTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class ComplexPropagationTest extends AbstractComplexPropagationTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + ComplexPropagationTest() { + super(testing, null); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamProducerTest.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamProducerTest.java new file mode 100644 index 000000000000..aea60a23fb7b --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamProducerTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + SpringCloudStreamProducerTest() { + super(testing, null); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamRabbitTest.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamRabbitTest.java new file mode 100644 index 000000000000..0a48f66a0a33 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamRabbitTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + SpringCloudStreamRabbitTest() { + super(testing, null); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationAndRabbitTest.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationAndRabbitTest.java new file mode 100644 index 000000000000..cf1f7882ba4e --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationAndRabbitTest.java @@ -0,0 +1,155 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.semconv.NetworkAttributes; +import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringIntegrationAndRabbitTest { + + @RegisterExtension RabbitExtension rabbit; + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + SpringIntegrationAndRabbitTest() { + rabbit = new RabbitExtension(null); + } + + @Test + void shouldCooperateWithExistingRabbitMqInstrumentation() { + testing.waitForTraces(13); // from rabbitmq instrumentation of startup + testing.clearData(); + + runWithSpan("parent", () -> rabbit.getBean("producer", Runnable.class).run()); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasTotalAttributeCount(0), + span -> + span.hasName("producer").hasParent(trace.getSpan(0)).hasTotalAttributeCount(0), + span -> span.hasName("exchange.declare"), + span -> + span.hasName("exchange.declare") + .hasParent(trace.getSpan(1)) + .hasKind(SpanKind.CLIENT) + .hasAttributesSatisfyingExactly( + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + l -> l.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)), + equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq")), + span -> span.hasName("queue.declare"), + span -> span.hasName("queue.bind"), + span -> + span.hasName("testTopic publish") + .hasParent(trace.getSpan(1)) + .hasKind(SpanKind.PRODUCER) + .hasAttributesSatisfyingExactly( + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + l -> l.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)), + equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"), + equalTo( + MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME, + "testTopic"), + equalTo(MessagingIncubatingAttributes.MESSAGING_OPERATION, "publish"), + satisfies( + MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE, + l -> l.isInstanceOf(Long.class)), + satisfies( + MessagingIncubatingAttributes + .MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY, + s -> s.isInstanceOf(String.class))), + // spring-cloud-stream-binder-rabbit listener puts all messages into a BlockingQueue + // immediately after receiving + // that's why the rabbitmq CONSUMER span will never have any child span (and + // propagate context, actually) + span -> + span.satisfies( + spanData -> + assertThat(spanData.getName()) + .matches("testTopic.anonymous.[-\\w]+ process")) + .hasParent(trace.getSpan(6)) + .hasKind(SpanKind.CONSUMER) + .hasAttributesSatisfyingExactly( + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + l -> l.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)), + equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"), + equalTo( + MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME, + "testTopic"), + equalTo(MessagingIncubatingAttributes.MESSAGING_OPERATION, "process"), + satisfies( + MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE, + l -> l.isInstanceOf(Long.class)), + satisfies( + MessagingIncubatingAttributes + .MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY, + s -> s.isInstanceOf(String.class))), + // spring-integration will detect that spring-rabbit has already created a consumer + // span and back off + span -> + span.hasName("testTopic process") + .hasParent(trace.getSpan(6)) + .hasKind(SpanKind.CONSUMER) + .hasAttributesSatisfyingExactly( + equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"), + equalTo( + MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME, + "testTopic"), + equalTo(MessagingIncubatingAttributes.MESSAGING_OPERATION, "process"), + satisfies( + MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID, + s -> s.isInstanceOf(String.class)), + satisfies( + MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE, + l -> l.isInstanceOf(Long.class))), + span -> + span.hasName("consumer").hasParent(trace.getSpan(8)).hasTotalAttributeCount(0)), + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("basic.ack") + .hasKind(SpanKind.CLIENT) + .hasAttributesSatisfyingExactly( + satisfies( + NetworkAttributes.NETWORK_PEER_ADDRESS, + s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + l -> l.isInstanceOf(Long.class)), + satisfies( + NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)), + equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq")))); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationTelemetryTest.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationTelemetryTest.java new file mode 100644 index 000000000000..2c4f50424ead --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationTelemetryTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + SpringIntegrationTelemetryTest() { + super(testing, null); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/ComplexPropagationTest.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/ComplexPropagationTest.groovy deleted file mode 100644 index ff40c296cf51..000000000000 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/ComplexPropagationTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.LibraryTestTrait - -class ComplexPropagationTest extends AbstractComplexPropagationTest implements LibraryTestTrait { - @Override - Class additionalContextClass() { - GlobalInterceptorSpringConfig - } -} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy deleted file mode 100644 index a73da27e7bc5..000000000000 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.integration.config.GlobalChannelInterceptor -import org.springframework.messaging.support.ChannelInterceptor - -import static java.util.Collections.singletonList - -@Configuration -class GlobalInterceptorSpringConfig { - - @GlobalChannelInterceptor - @Bean - ChannelInterceptor otelInterceptor() { - SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedHeaders(singletonList("test-message-header")) - .build() - .newChannelInterceptor() - } -} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorWithProducerSpanSpringConfig.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorWithProducerSpanSpringConfig.groovy deleted file mode 100644 index 92a18d24804e..000000000000 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorWithProducerSpanSpringConfig.groovy +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.integration.config.GlobalChannelInterceptor -import org.springframework.messaging.support.ChannelInterceptor - -@Configuration -class GlobalInterceptorWithProducerSpanSpringConfig { - - @GlobalChannelInterceptor - @Bean - ChannelInterceptor otelInterceptor() { - SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) - .setProducerSpanEnabled(true) - .build() - .newChannelInterceptor() - } -} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringCloudStreamProducerTest.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringCloudStreamProducerTest.groovy deleted file mode 100644 index 33427f9196b8..000000000000 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringCloudStreamProducerTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.LibraryTestTrait - -class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest implements LibraryTestTrait { - @Override - Class additionalContextClass() { - GlobalInterceptorWithProducerSpanSpringConfig - } -} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringCloudStreamRabbitTest.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringCloudStreamRabbitTest.groovy deleted file mode 100644 index e9d659f21778..000000000000 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringCloudStreamRabbitTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.LibraryTestTrait - -class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest implements LibraryTestTrait { - @Override - Class additionalContextClass() { - GlobalInterceptorSpringConfig - } -} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringIntegrationTelemetryTest.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringIntegrationTelemetryTest.groovy deleted file mode 100644 index b81fbcf40231..000000000000 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/SpringIntegrationTelemetryTest.groovy +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.LibraryTestTrait - -class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest implements LibraryTestTrait { - @Override - Class additionalContextClass() { - GlobalInterceptorSpringConfig - } -} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/ComplexPropagationTest.java b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/ComplexPropagationTest.java new file mode 100644 index 000000000000..52bf2c4807f3 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/ComplexPropagationTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class ComplexPropagationTest extends AbstractComplexPropagationTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + public ComplexPropagationTest() { + super(testing, GlobalInterceptorSpringConfig.class); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/GlobalInterceptorSpringConfig.java b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/GlobalInterceptorSpringConfig.java new file mode 100644 index 000000000000..49e7c8c04e22 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/GlobalInterceptorSpringConfig.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import static java.util.Collections.singletonList; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.config.GlobalChannelInterceptor; +import org.springframework.messaging.support.ChannelInterceptor; + +@Configuration +class GlobalInterceptorSpringConfig { + + @GlobalChannelInterceptor + @Bean + ChannelInterceptor otelInterceptor() { + return SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders(singletonList("test-message-header")) + .build() + .newChannelInterceptor(); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/GlobalInterceptorWithProducerSpanSpringConfig.java b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/GlobalInterceptorWithProducerSpanSpringConfig.java new file mode 100644 index 000000000000..68784dcec635 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/GlobalInterceptorWithProducerSpanSpringConfig.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.config.GlobalChannelInterceptor; +import org.springframework.messaging.support.ChannelInterceptor; + +@Configuration +class GlobalInterceptorWithProducerSpanSpringConfig { + + @GlobalChannelInterceptor + @Bean + ChannelInterceptor otelInterceptor() { + return SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) + .setProducerSpanEnabled(true) + .build() + .newChannelInterceptor(); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamProducerTest.java b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamProducerTest.java new file mode 100644 index 000000000000..b2f25b143045 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamProducerTest.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + public SpringCloudStreamProducerTest() { + super(testing, GlobalInterceptorWithProducerSpanSpringConfig.class); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamRabbitTest.java b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamRabbitTest.java new file mode 100644 index 000000000000..da78855adde3 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringCloudStreamRabbitTest.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + public SpringCloudStreamRabbitTest() { + super(testing, GlobalInterceptorSpringConfig.class); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationTelemetryTest.java b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationTelemetryTest.java new file mode 100644 index 000000000000..f8b3dda8f264 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/SpringIntegrationTelemetryTest.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + public SpringIntegrationTelemetryTest() { + super(testing, GlobalInterceptorSpringConfig.class); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy deleted file mode 100644 index 0d1ce31d9a9f..000000000000 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import org.springframework.boot.SpringApplication -import org.springframework.boot.SpringBootConfiguration -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.context.event.ApplicationReadyEvent -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.context.event.EventListener -import org.springframework.integration.channel.DirectChannel -import org.springframework.integration.channel.ExecutorChannel -import org.springframework.messaging.Message -import org.springframework.messaging.SubscribableChannel -import org.springframework.messaging.support.MessageBuilder -import spock.lang.Shared - -import java.util.concurrent.BlockingQueue -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors -import java.util.concurrent.LinkedBlockingQueue -import java.util.stream.Collectors - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER - -abstract class AbstractComplexPropagationTest extends InstrumentationSpecification { - - abstract Class additionalContextClass() - - @Shared - ConfigurableApplicationContext applicationContext - - def setupSpec() { - def contextClasses = [ExternalQueueConfig] - if (additionalContextClass() != null) { - contextClasses += additionalContextClass() - } - - def app = new SpringApplication(contextClasses as Class[]) - app.setDefaultProperties([ - "spring.main.web-application-type": "none" - ]) - applicationContext = app.run() - } - - def cleanupSpec() { - applicationContext?.close() - } - - def "should propagate context through a custom message queue"() { - given: - def sendChannel = applicationContext.getBean("sendChannel", SubscribableChannel) - def receiveChannel = applicationContext.getBean("receiveChannel", SubscribableChannel) - - def messageHandler = new CapturingMessageHandler() - receiveChannel.subscribe(messageHandler) - - when: - sendChannel.send(MessageBuilder.withPayload("test") - .setHeader("theAnswer", "42") - .build()) - - then: - messageHandler.join() - - assertTraces(1) { - trace(0, 3) { - // there's no span in the context, so spring-integration adds a CONSUMER one - span(0) { - name "application.sendChannel process" - kind CONSUMER - } - // message is received in a separate thread without any context, so a CONSUMER span with parent - // extracted from the incoming message is created - span(1) { - name "application.receiveChannel process" - childOf span(0) - kind CONSUMER - } - span(2) { - name "handler" - childOf span(1) - } - } - } - - cleanup: - receiveChannel.unsubscribe(messageHandler) - } - - // this setup simulates separate producer/consumer and some "external" message queue in between - @SpringBootConfiguration - @EnableAutoConfiguration - static class ExternalQueueConfig { - @Bean - SubscribableChannel sendChannel() { - new ExecutorChannel(Executors.newSingleThreadExecutor()) - } - - @Bean - SubscribableChannel receiveChannel() { - new DirectChannel() - } - - @Bean - BlockingQueue externalQueue() { - new LinkedBlockingQueue() - } - - @Bean(destroyMethod = "shutdownNow") - ExecutorService consumerThread() { - Executors.newSingleThreadExecutor() - } - - @EventListener(ApplicationReadyEvent) - void initialize() { - sendChannel().subscribe { message -> - externalQueue().offer(Payload.from(message)) - } - - consumerThread().execute({ - while (!Thread.interrupted()) { - def payload = externalQueue().take() - receiveChannel().send(payload.toMessage()) - } - }) - } - } - - static class Payload { - String body - Map headers - - static Payload from(Message message) { - def body = message.payload as String - Map headers = message.headers.entrySet().stream() - .filter({ kv -> kv.value instanceof String }) - .collect(Collectors.toMap({ it.key }, { it.value })) - new Payload(body: body, headers: headers) - } - - Message toMessage() { - MessageBuilder.withPayload(body) - .copyHeaders(headers) - .build() - } - } -} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringCloudStreamProducerTest.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringCloudStreamProducerTest.groovy deleted file mode 100644 index 5a7f3f99995b..000000000000 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringCloudStreamProducerTest.groovy +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.InstrumentationSpecification - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.PRODUCER -import static org.junit.jupiter.api.Assumptions.assumeTrue - -abstract class AbstractSpringCloudStreamProducerTest extends InstrumentationSpecification implements WithRabbitProducerConsumerTrait { - private static final boolean HAS_PRODUCER_SPAN = Boolean.getBoolean("otel.instrumentation.spring-integration.producer.enabled") - - abstract Class additionalContextClass() - - def setupSpec() { - startRabbit(additionalContextClass()) - } - - def cleanupSpec() { - stopRabbit() - } - - def "has producer span"() { - assumeTrue(HAS_PRODUCER_SPAN) - - when: - producerContext.getBean("producer", Runnable).run() - - then: - assertTraces(1) { - trace(0, 4) { - span(0) { - name "producer" - } - span(1) { - name "testProducer.output publish" - childOf span(0) - kind PRODUCER - } - span(2) { - name "testConsumer.input process" - childOf span(1) - kind CONSUMER - } - span(3) { - name "consumer" - childOf span(2) - } - } - } - } -} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringCloudStreamRabbitTest.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringCloudStreamRabbitTest.groovy deleted file mode 100644 index 577a35a65691..000000000000 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringCloudStreamRabbitTest.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.InstrumentationSpecification - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER - -abstract class AbstractSpringCloudStreamRabbitTest extends InstrumentationSpecification implements WithRabbitProducerConsumerTrait { - - abstract Class additionalContextClass() - - def setupSpec() { - startRabbit(additionalContextClass()) - } - - def cleanupSpec() { - stopRabbit() - } - - def "should propagate context through RabbitMQ"() { - when: - producerContext.getBean("producer", Runnable).run() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - name "producer" - } - span(1) { - name "testConsumer.input process" - childOf span(0) - kind CONSUMER - } - span(2) { - name "consumer" - childOf span(1) - } - } - } - } -} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy deleted file mode 100644 index 92f26657e2ae..000000000000 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.sdk.trace.data.SpanData -import org.springframework.boot.SpringApplication -import org.springframework.boot.SpringBootConfiguration -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.context.event.ApplicationReadyEvent -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.context.event.EventListener -import org.springframework.integration.channel.DirectChannel -import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper -import org.springframework.messaging.Message -import org.springframework.messaging.SubscribableChannel -import org.springframework.messaging.support.ExecutorSubscribableChannel -import org.springframework.messaging.support.MessageBuilder -import spock.lang.Shared -import spock.lang.Unroll - -import java.util.concurrent.Executors - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER - -@Unroll -abstract class AbstractSpringIntegrationTracingTest extends InstrumentationSpecification { - - abstract Class additionalContextClass() - - @Shared - ConfigurableApplicationContext applicationContext - - def setupSpec() { - def contextClasses = [MessageChannelsConfig] - if (additionalContextClass() != null) { - contextClasses += additionalContextClass() - } - - def app = new SpringApplication(contextClasses as Class[]) - app.setDefaultProperties([ - "spring.main.web-application-type": "none" - ]) - applicationContext = app.run() - } - - def cleanupSpec() { - applicationContext?.close() - } - - def "should propagate context (#channelName)"() { - given: - def channel = applicationContext.getBean(channelName, SubscribableChannel) - - def messageHandler = new CapturingMessageHandler() - channel.subscribe(messageHandler) - - when: - channel.send(MessageBuilder.withPayload("test") - .build()) - - then: - def capturedMessage = messageHandler.join() - - assertTraces(1) { - trace(0, 2) { - span(0) { - name interceptorSpanName - kind CONSUMER - } - span(1) { - name "handler" - childOf span(0) - } - - def interceptorSpan = span(0) - verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan) - } - } - - cleanup: - channel.unsubscribe(messageHandler) - - where: - channelName | interceptorSpanName - "directChannel" | "application.directChannel process" - "executorChannel" | "executorChannel process" - } - - def "should not add interceptor twice"() { - given: - def channel = applicationContext.getBean("directChannel1", SubscribableChannel) - - def messageHandler = new CapturingMessageHandler() - channel.subscribe(messageHandler) - - when: - channel.send(MessageBuilder.withPayload("test") - .build()) - - then: - def capturedMessage = messageHandler.join() - - assertTraces(1) { - trace(0, 2) { - span(0) { - // the channel name is overwritten by the last bean registration - name "application.directChannel2 process" - kind CONSUMER - } - span(1) { - name "handler" - childOf span(0) - } - - def interceptorSpan = span(0) - verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan) - } - } - - cleanup: - channel.unsubscribe(messageHandler) - } - - def "should not create a span when there is already a span in the context"() { - given: - def channel = applicationContext.getBean("directChannel", SubscribableChannel) - - def messageHandler = new CapturingMessageHandler() - channel.subscribe(messageHandler) - - when: - runWithSpan("parent") { - channel.send(MessageBuilder.withPayload("test") - .build()) - } - - then: - messageHandler.join() - - assertTraces(1) { - trace(0, 2) { - span(0) { - name "parent" - } - span(1) { - name "handler" - childOf span(0) - } - } - } - - cleanup: - channel.unsubscribe(messageHandler) - } - - def "should handle multiple message channels in a chain"() { - given: - def channel1 = applicationContext.getBean("linkedChannel1", SubscribableChannel) - def channel2 = applicationContext.getBean("linkedChannel2", SubscribableChannel) - - def messageHandler = new CapturingMessageHandler() - channel2.subscribe(messageHandler) - - when: - channel1.send(MessageBuilder.withPayload("test") - .build()) - - then: - def capturedMessage = messageHandler.join() - - assertTraces(1) { - trace(0, 2) { - span(0) { - name "application.linkedChannel1 process" - kind CONSUMER - } - span(1) { - name "handler" - childOf span(0) - } - - def lastChannelSpan = span(0) - verifyCorrectSpanWasPropagated(capturedMessage, lastChannelSpan) - } - } - - cleanup: - channel2.unsubscribe(messageHandler) - } - - def "capture message header"() { - given: - def channel = applicationContext.getBean("directChannel", SubscribableChannel) - - def messageHandler = new CapturingMessageHandler() - channel.subscribe(messageHandler) - - when: - channel.send(MessageBuilder.withPayload("test") - .setHeader("test-message-header", "test") - .build()) - - then: - def capturedMessage = messageHandler.join() - - assertTraces(1) { - trace(0, 2) { - span(0) { - name "application.directChannel process" - kind CONSUMER - } - span(1) { - name "handler" - childOf span(0) - } - - def interceptorSpan = span(0) - verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan) - } - } - - cleanup: - channel.unsubscribe(messageHandler) - } - - static void verifyCorrectSpanWasPropagated(Message capturedMessage, SpanData parentSpan) { - def propagatedSpan = capturedMessage.headers.get("traceparent") as String - assert propagatedSpan.contains(parentSpan.traceId), "wrong trace id" - assert propagatedSpan.contains(parentSpan.spanId), "wrong span id" - } - - @SpringBootConfiguration - @EnableAutoConfiguration - static class MessageChannelsConfig { - - SubscribableChannel problematicSharedChannel = new DirectChannel() - - @Bean - SubscribableChannel directChannel() { - new DirectChannel() - } - - @Bean - SubscribableChannel directChannel1() { - problematicSharedChannel - } - - @Bean - SubscribableChannel directChannel2() { - problematicSharedChannel - } - - @Bean - SubscribableChannel executorChannel(GlobalChannelInterceptorWrapper otelInterceptor) { - def channel = new ExecutorSubscribableChannel(Executors.newSingleThreadExecutor()) - if (!Boolean.getBoolean("testLatestDeps")) { - // spring does not inject the interceptor in 4.1 because ExecutorSubscribableChannel isn't ChannelInterceptorAware - // in later versions spring injects the global interceptor into InterceptableChannel (which ExecutorSubscribableChannel is) - channel.addInterceptor(otelInterceptor.channelInterceptor) - } - channel - } - - @Bean - SubscribableChannel linkedChannel1() { - new DirectChannel() - } - - @Bean - SubscribableChannel linkedChannel2() { - new DirectChannel() - } - - @EventListener(ApplicationReadyEvent) - void initialize() { - linkedChannel1().subscribe { message -> - linkedChannel2().send(message) - } - } - } -} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/CapturingMessageHandler.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/CapturingMessageHandler.groovy deleted file mode 100644 index 5e5985853d38..000000000000 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/CapturingMessageHandler.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.springframework.messaging.Message -import org.springframework.messaging.MessageHandler -import org.springframework.messaging.MessagingException - -import java.util.concurrent.CompletableFuture - -import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan - -class CapturingMessageHandler implements MessageHandler { - final CompletableFuture> captured = new CompletableFuture<>() - - @Override - void handleMessage(Message message) throws MessagingException { - runWithSpan("handler") { - captured.complete(message) - } - } - - Message join() { - captured.join() - } -} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/WithRabbitProducerConsumerTrait.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/WithRabbitProducerConsumerTrait.groovy deleted file mode 100644 index ef72c579d3d4..000000000000 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/WithRabbitProducerConsumerTrait.groovy +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.SpringApplication -import org.springframework.boot.SpringBootConfiguration -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.cloud.stream.annotation.EnableBinding -import org.springframework.cloud.stream.annotation.StreamListener -import org.springframework.cloud.stream.messaging.Sink -import org.springframework.cloud.stream.messaging.Source -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.messaging.support.MessageBuilder -import org.testcontainers.containers.GenericContainer -import org.testcontainers.containers.wait.strategy.Wait - -import java.time.Duration - -import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan - -trait WithRabbitProducerConsumerTrait { - - static GenericContainer rabbitMqContainer - static ConfigurableApplicationContext producerContext - static ConfigurableApplicationContext consumerContext - - def startRabbit(Class additionalContext = null) { - rabbitMqContainer = new GenericContainer('rabbitmq:latest') - .withExposedPorts(5672) - .waitingFor(Wait.forLogMessage(".*Server startup complete.*", 1)) - .withStartupTimeout(Duration.ofMinutes(2)) - rabbitMqContainer.start() - - def producerApp = new SpringApplication(getContextClasses(ProducerConfig, additionalContext)) - producerApp.setDefaultProperties([ - "spring.application.name" : "testProducer", - "spring.jmx.enabled" : false, - "spring.main.web-application-type" : "none", - "spring.rabbitmq.host" : rabbitMqContainer.host, - "spring.rabbitmq.port" : rabbitMqContainer.getMappedPort(5672), - "spring.cloud.stream.bindings.output.destination": "testTopic" - ]) - producerContext = producerApp.run() - - def consumerApp = new SpringApplication(getContextClasses(ConsumerConfig, additionalContext)) - consumerApp.setDefaultProperties([ - "spring.application.name" : "testConsumer", - "spring.jmx.enabled" : false, - "spring.main.web-application-type" : "none", - "spring.rabbitmq.host" : rabbitMqContainer.host, - "spring.rabbitmq.port" : rabbitMqContainer.getMappedPort(5672), - "spring.cloud.stream.bindings.input.destination": "testTopic" - ]) - consumerContext = consumerApp.run() - } - - private Class[] getContextClasses(Class mainContext, Class additionalContext) { - def contextClasses = [mainContext] - if (additionalContext != null) { - contextClasses += additionalContext - } - contextClasses - } - - def stopRabbit() { - rabbitMqContainer?.stop() - rabbitMqContainer = null - producerContext?.close() - producerContext = null - consumerContext?.close() - consumerContext = null - } - - @SpringBootConfiguration - @EnableAutoConfiguration - @EnableBinding(Source) - static class ProducerConfig { - @Autowired - Source source - - @Bean - Runnable producer() { - return { - runWithSpan("producer") { - source.output().send(MessageBuilder.withPayload("test").build()) - } - } - } - } - - @SpringBootConfiguration - @EnableAutoConfiguration - @EnableBinding(Sink) - static class ConsumerConfig { - @StreamListener(Sink.INPUT) - void consume(String ignored) { - runWithSpan("consumer") {} - } - } -} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractComplexPropagationTest.java b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractComplexPropagationTest.java new file mode 100644 index 000000000000..11537efb0b42 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractComplexPropagationTest.java @@ -0,0 +1,162 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.event.EventListener; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.ExecutorChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.SubscribableChannel; + +public abstract class AbstractComplexPropagationTest { + + private final Class additionalContextClass; + protected InstrumentationExtension testing; + + ConfigurableApplicationContext applicationContext; + + public AbstractComplexPropagationTest( + InstrumentationExtension testing, @Nullable Class additionalContextClass) { + this.testing = testing; + this.additionalContextClass = additionalContextClass; + } + + @BeforeEach + void setUp() { + List> contextClasses = new ArrayList<>(); + contextClasses.add(ExternalQueueConfig.class); + if (additionalContextClass != null) { + contextClasses.add(additionalContextClass); + } + SpringApplication springApplication = + new SpringApplication(contextClasses.toArray(new Class[0])); + springApplication.setDefaultProperties( + Collections.singletonMap("spring.main.web-application-type", "none")); + applicationContext = springApplication.run(); + } + + @AfterEach + void tearDown() { + if (applicationContext != null) { + applicationContext.close(); + } + } + + @Test + void shouldPropagateContextThroughAcomplexFlow() { + SubscribableChannel sendChannel = + applicationContext.getBean("sendChannel", SubscribableChannel.class); + SubscribableChannel receiveChannel = + applicationContext.getBean("receiveChannel", SubscribableChannel.class); + + CapturingMessageHandler messageHandler = new CapturingMessageHandler(); + receiveChannel.subscribe(messageHandler); + + sendChannel.send(MessageBuilder.withPayload("test").setHeader("theAnswer", "42").build()); + + messageHandler.join(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("application.sendChannel process").hasKind(SpanKind.CONSUMER), + span -> + span.hasName("application.receiveChannel process") + .hasParent(trace.getSpan(0)) + .hasKind(SpanKind.CONSUMER), + span -> span.hasName("handler").hasParent(trace.getSpan(1)))); + + receiveChannel.unsubscribe(messageHandler); + } + + // this setup simulates separate producer/consumer and some "external" message queue in between + @SpringBootConfiguration + @EnableAutoConfiguration + static class ExternalQueueConfig { + @Bean + SubscribableChannel sendChannel() { + return new ExecutorChannel(Executors.newSingleThreadExecutor()); + } + + @Bean + SubscribableChannel receiveChannel() { + return new DirectChannel(); + } + + @Bean + BlockingQueue externalQueue() { + return new LinkedBlockingQueue<>(); + } + + @Bean(destroyMethod = "shutdownNow") + ExecutorService consumerThread() { + return Executors.newSingleThreadExecutor(); + } + + @EventListener(ApplicationReadyEvent.class) + void initialize() { + sendChannel().subscribe(message -> externalQueue().offer(Payload.from(message))); + + consumerThread() + .execute( + () -> { + while (!Thread.interrupted()) { + try { + Payload payload = externalQueue().take(); + receiveChannel().send(payload.toMessage()); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + }); + } + } + + static class Payload { + String body; + Map headers; + + Payload(String body, Map headers) { + this.body = body; + this.headers = headers; + } + + static Payload from(Message message) { + String body = (String) message.getPayload(); + Map headers = + message.getHeaders().entrySet().stream() + .filter(kv -> kv.getValue() instanceof String) + .collect(Collectors.toMap(Map.Entry::getKey, kv -> (String) kv.getValue())); + return new Payload(body, headers); + } + + Message toMessage() { + return MessageBuilder.withPayload(body).copyHeaders(headers).build(); + } + } +} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringCloudStreamProducerTest.java b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringCloudStreamProducerTest.java new file mode 100644 index 000000000000..e0c24f5c69b2 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringCloudStreamProducerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class AbstractSpringCloudStreamProducerTest { + + @RegisterExtension RabbitExtension rabbit; + + protected final InstrumentationExtension testing; + + private static final boolean HAS_PRODUCER_SPAN = + Boolean.getBoolean("otel.instrumentation.spring-integration.producer.enabled"); + + public AbstractSpringCloudStreamProducerTest( + InstrumentationExtension testing, Class additionalContextClass) { + this.testing = testing; + rabbit = new RabbitExtension(additionalContextClass); + } + + @Test + void hasProducerSpan() { + assumeTrue(HAS_PRODUCER_SPAN); + + rabbit.getBean("producer", Runnable.class).run(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer").hasKind(SpanKind.INTERNAL), + span -> + span.hasName("testProducer.output publish") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("testConsumer.input process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(1)), + span -> + span.hasName("consumer") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)))); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringCloudStreamRabbitTest.java b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringCloudStreamRabbitTest.java new file mode 100644 index 000000000000..5020e8b7abb8 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringCloudStreamRabbitTest.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class AbstractSpringCloudStreamRabbitTest { + + @RegisterExtension RabbitExtension rabbit; + + protected final InstrumentationExtension testing; + + public AbstractSpringCloudStreamRabbitTest( + InstrumentationExtension testing, Class additionalContextClass) { + this.testing = testing; + rabbit = new RabbitExtension(additionalContextClass); + } + + @Test + void shouldPropagateContextThroughRabbitMq() { + rabbit.getBean("producer", Runnable.class).run(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer").hasKind(SpanKind.INTERNAL), + span -> + span.hasName("testConsumer.input process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("consumer") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)))); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringIntegrationTracingTest.java b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringIntegrationTracingTest.java new file mode 100644 index 000000000000..3bc3ae5a5eec --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/AbstractSpringIntegrationTracingTest.java @@ -0,0 +1,257 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.trace.data.SpanData; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executors; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.event.EventListener; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper; +import org.springframework.messaging.Message; +import org.springframework.messaging.SubscribableChannel; +import org.springframework.messaging.support.ExecutorSubscribableChannel; +import org.springframework.messaging.support.MessageBuilder; + +abstract class AbstractSpringIntegrationTracingTest { + + protected final InstrumentationExtension testing; + + private final Class additionalContextClass; + + ConfigurableApplicationContext applicationContext; + + public AbstractSpringIntegrationTracingTest( + InstrumentationExtension testing, Class additionalContextClass) { + this.testing = testing; + this.additionalContextClass = additionalContextClass; + } + + @BeforeEach + public void setUp() { + List> contextClasses = new ArrayList<>(); + contextClasses.add(MessageChannelsConfig.class); + if (additionalContextClass != null) { + contextClasses.add(additionalContextClass); + } + SpringApplication springApplication = + new SpringApplication(contextClasses.toArray(new Class[0])); + springApplication.setDefaultProperties( + Collections.singletonMap("spring.main.web-application-type", "none")); + applicationContext = springApplication.run(); + } + + @AfterEach + public void tearDown() { + if (applicationContext != null) { + applicationContext.close(); + } + } + + @ParameterizedTest + @CsvSource( + value = { + "directChannel,application.directChannel process", + "executorChannel,executorChannel process" + }, + delimiter = ',') + public void shouldPropagateContext(String channelName, String interceptorSpanName) { + SubscribableChannel channel = + applicationContext.getBean(channelName, SubscribableChannel.class); + + CapturingMessageHandler messageHandler = new CapturingMessageHandler(); + channel.subscribe(messageHandler); + + channel.send(MessageBuilder.withPayload("test").build()); + + Message capturedMessage = messageHandler.join(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + span.hasName(interceptorSpanName).hasKind(SpanKind.CONSUMER); + verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0)); + }, + span -> span.hasName("handler").hasParent(trace.getSpan(0)))); + + channel.unsubscribe(messageHandler); + } + + @Test + void shouldNotAddInterceptorTwice() { + SubscribableChannel channel = + applicationContext.getBean("directChannel1", SubscribableChannel.class); + + CapturingMessageHandler messageHandler = new CapturingMessageHandler(); + channel.subscribe(messageHandler); + + channel.send(MessageBuilder.withPayload("test").build()); + + Message capturedMessage = messageHandler.join(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + span.hasName("application.directChannel2 process").hasKind(SpanKind.CONSUMER); + verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0)); + }, + span -> span.hasName("handler").hasParent(trace.getSpan(0)))); + + channel.unsubscribe(messageHandler); + } + + @Test + void shouldNotCreateAspanWhenThereIsAlreadyAspanInTheContext() { + SubscribableChannel channel = + applicationContext.getBean("directChannel", SubscribableChannel.class); + + CapturingMessageHandler messageHandler = new CapturingMessageHandler(); + channel.subscribe(messageHandler); + + testing.runWithSpan( + "parent", + () -> { + channel.send(MessageBuilder.withPayload("test").build()); + }); + + messageHandler.join(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent"), + span -> span.hasName("handler").hasParent(trace.getSpan(0)))); + + channel.unsubscribe(messageHandler); + } + + @Test + void shouldHandleMultipleMessageChannelsInAchain() { + SubscribableChannel channel1 = + applicationContext.getBean("linkedChannel1", SubscribableChannel.class); + SubscribableChannel channel2 = + applicationContext.getBean("linkedChannel2", SubscribableChannel.class); + + CapturingMessageHandler messageHandler = new CapturingMessageHandler(); + channel2.subscribe(messageHandler); + + channel1.send(MessageBuilder.withPayload("test").build()); + + Message capturedMessage = messageHandler.join(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + span.hasName("application.linkedChannel1 process").hasKind(SpanKind.CONSUMER); + verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0)); + }, + span -> span.hasName("handler").hasParent(trace.getSpan(0)))); + + channel2.unsubscribe(messageHandler); + } + + @Test + void captureMessageHeader() { + SubscribableChannel channel = + applicationContext.getBean("directChannel", SubscribableChannel.class); + + CapturingMessageHandler messageHandler = new CapturingMessageHandler(); + channel.subscribe(messageHandler); + + channel.send( + MessageBuilder.withPayload("test").setHeader("test-message-header", "test").build()); + + Message capturedMessage = messageHandler.join(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + span.hasName("application.directChannel process").hasKind(SpanKind.CONSUMER); + verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0)); + }, + span -> span.hasName("handler").hasParent(trace.getSpan(0)))); + + channel.unsubscribe(messageHandler); + } + + static void verifyCorrectSpanWasPropagated(Message capturedMessage, SpanData parentSpan) { + String propagatedSpan = (String) capturedMessage.getHeaders().get("traceparent"); + assertThat(propagatedSpan).contains(parentSpan.getTraceId()); + assertThat(propagatedSpan).contains(parentSpan.getSpanId()); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + public static class MessageChannelsConfig { + + SubscribableChannel problematicSharedChannel = new DirectChannel(); + + @Bean + public SubscribableChannel directChannel() { + return new DirectChannel(); + } + + @Bean + public SubscribableChannel directChannel1() { + return problematicSharedChannel; + } + + @Bean + public SubscribableChannel directChannel2() { + return problematicSharedChannel; + } + + @Bean + public SubscribableChannel executorChannel(GlobalChannelInterceptorWrapper otelInterceptor) { + ExecutorSubscribableChannel channel = + new ExecutorSubscribableChannel(Executors.newSingleThreadExecutor()); + if (!Boolean.getBoolean("testLatestDeps")) { + // spring does not inject the interceptor in 4.1 because ExecutorSubscribableChannel isn't + // ChannelInterceptorAware + // in later versions spring injects the global interceptor into InterceptableChannel (which + // ExecutorSubscribableChannel is) + channel.addInterceptor(otelInterceptor.getChannelInterceptor()); + } + return channel; + } + + @Bean + public SubscribableChannel linkedChannel1() { + return new DirectChannel(); + } + + @Bean + public SubscribableChannel linkedChannel2() { + return new DirectChannel(); + } + + @EventListener(ApplicationReadyEvent.class) + public void initialize() { + linkedChannel1().subscribe(message -> linkedChannel2().send(message)); + } + } +} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/CapturingMessageHandler.java b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/CapturingMessageHandler.java new file mode 100644 index 000000000000..cb059f63dead --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/CapturingMessageHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; + +import java.util.concurrent.CompletableFuture; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHandler; + +class CapturingMessageHandler implements MessageHandler { + final CompletableFuture> captured = new CompletableFuture<>(); + + @Override + public void handleMessage(Message message) { + runWithSpan( + "handler", + () -> { + captured.complete(message); + }); + } + + Message join() { + return captured.join(); + } +} diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/RabbitExtension.java b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/RabbitExtension.java new file mode 100644 index 000000000000..b1b90ad47503 --- /dev/null +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/v4_1/RabbitExtension.java @@ -0,0 +1,135 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1; + +import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.annotation.StreamListener; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.cloud.stream.messaging.Source; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.messaging.support.MessageBuilder; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +public class RabbitExtension implements BeforeEachCallback, AfterEachCallback { + + private GenericContainer rabbitMqContainer; + protected ConfigurableApplicationContext producerContext; + private ConfigurableApplicationContext consumerContext; + + private final Class additionalContextClass; + + public RabbitExtension(Class additionalContextClass) { + this.additionalContextClass = additionalContextClass; + } + + public T getBean(String name, Class requiredType) { + return producerContext.getBean(name, requiredType); + } + + @Override + public void beforeEach(ExtensionContext context) { + rabbitMqContainer = + new GenericContainer<>("rabbitmq:latest") + .withExposedPorts(5672) + .waitingFor(Wait.forLogMessage(".*Server startup complete.*", 1)) + .withStartupTimeout(Duration.ofMinutes(2)); + rabbitMqContainer.start(); + + SpringApplication producerApp = + new SpringApplication(getContextClasses(ProducerConfig.class, additionalContextClass)); + Map producerProperties = new HashMap<>(); + producerProperties.put("spring.application.name", "testProducer"); + producerProperties.put("spring.jmx.enabled", false); + producerProperties.put("spring.main.web-application-type", "none"); + producerProperties.put("spring.rabbitmq.host", rabbitMqContainer.getHost()); + producerProperties.put("spring.rabbitmq.port", rabbitMqContainer.getMappedPort(5672)); + producerProperties.put("spring.cloud.stream.bindings.output.destination", "testTopic"); + producerApp.setDefaultProperties(producerProperties); + producerContext = producerApp.run(); + + SpringApplication consumerApp = + new SpringApplication( + getContextClasses(ProducerConfig.ConsumerConfig.class, additionalContextClass)); + Map consumerProperties = new HashMap<>(); + consumerProperties.put("spring.application.name", "testConsumer"); + consumerProperties.put("spring.jmx.enabled", false); + consumerProperties.put("spring.main.web-application-type", "none"); + consumerProperties.put("spring.rabbitmq.host", rabbitMqContainer.getHost()); + consumerProperties.put("spring.rabbitmq.port", rabbitMqContainer.getMappedPort(5672)); + consumerProperties.put("spring.cloud.stream.bindings.input.destination", "testTopic"); + consumerApp.setDefaultProperties(consumerProperties); + consumerContext = consumerApp.run(); + } + + @Override + public void afterEach(ExtensionContext context) { + if (rabbitMqContainer != null) { + rabbitMqContainer.stop(); + } + if (producerContext != null) { + producerContext.close(); + } + if (consumerContext != null) { + consumerContext.close(); + } + } + + private static Class[] getContextClasses(Class mainContext, Class additionalContext) { + List> contextClasses = new ArrayList<>(); + contextClasses.add(mainContext); + if (additionalContext != null) { + contextClasses.add(additionalContext); + } + return contextClasses.toArray(new Class[0]); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @EnableBinding(Source.class) + static class ProducerConfig { + @Autowired Source source; + + @Bean + Runnable producer() { + return () -> + runWithSpan( + "producer", + () -> { + source.output().send(MessageBuilder.withPayload("test").build()); + }); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @EnableBinding(Sink.class) + static class ConsumerConfig { + @StreamListener(Sink.INPUT) + void consume(String ignored) { + runWithSpan( + "consumer", + () -> { + // do nothing + }); + } + } + } +} diff --git a/instrumentation/spring/spring-web/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/v3_1/SpringWebTelemetryBuilder.java b/instrumentation/spring/spring-web/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/v3_1/SpringWebTelemetryBuilder.java index 3eab8e96bf61..36fde223c3ec 100644 --- a/instrumentation/spring/spring-web/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/v3_1/SpringWebTelemetryBuilder.java +++ b/instrumentation/spring/spring-web/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/v3_1/SpringWebTelemetryBuilder.java @@ -74,7 +74,9 @@ public SpringWebTelemetryBuilder setCapturedResponseHeaders(List respons /** Sets custom {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public SpringWebTelemetryBuilder setSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function< + SpanNameExtractor, + ? extends SpanNameExtractor> spanNameExtractorTransformer) { builder.setSpanNameExtractor(spanNameExtractorTransformer); return this; diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ContextHandlerInstrumentation.java b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ContextHandlerInstrumentation.java index aa51160e5fc0..f1d6af9e73d6 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ContextHandlerInstrumentation.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ContextHandlerInstrumentation.java @@ -10,11 +10,10 @@ import io.netty.channel.Channel; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import java.util.Deque; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -39,8 +38,7 @@ public static class CreateOperationsAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static Scope onEnter(@Advice.Argument(0) Channel channel) { // set context to the first unprocessed request - Deque serverContextx = channel.attr(AttributeKeys.SERVER_CONTEXT).get(); - ServerContext serverContext = serverContextx != null ? serverContextx.peekFirst() : null; + ServerContext serverContext = ServerContexts.peekFirst(channel); if (serverContext != null) { return serverContext.context().makeCurrent(); } diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/HttpTrafficHandlerInstrumentation.java b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/HttpTrafficHandlerInstrumentation.java index efe171194b18..3567a8524644 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/HttpTrafficHandlerInstrumentation.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/HttpTrafficHandlerInstrumentation.java @@ -10,11 +10,10 @@ import io.netty.channel.ChannelHandlerContext; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import java.util.Deque; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -40,9 +39,7 @@ public static class RunAdvice { public static Scope onEnter( @Advice.FieldValue("ctx") ChannelHandlerContext channelHandlerContext) { // set context to the first unprocessed request - Deque serverContexts = - channelHandlerContext.channel().attr(AttributeKeys.SERVER_CONTEXT).get(); - ServerContext serverContext = serverContexts != null ? serverContexts.peekFirst() : null; + ServerContext serverContext = ServerContexts.peekFirst(channelHandlerContext.channel()); if (serverContext != null) { return serverContext.context().makeCurrent(); } diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ReactorNettyInstrumentationModule.java b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ReactorNettyInstrumentationModule.java index 047603fc19d4..8abd3d0a954e 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ReactorNettyInstrumentationModule.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ReactorNettyInstrumentationModule.java @@ -8,20 +8,22 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.Arrays; import java.util.List; @AutoService(InstrumentationModule.class) -public class ReactorNettyInstrumentationModule extends InstrumentationModule { +public class ReactorNettyInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public ReactorNettyInstrumentationModule() { super("reactor-netty", "reactor-netty-server"); } @Override - public boolean isIndyModule() { - // uses classes from netty instrumentation that are now in a different class loader - return false; + public String getModuleGroup() { + // relies on netty + return "netty"; } @Override diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java index e8387880cd32..e004ee7e9a70 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java @@ -169,7 +169,9 @@ public SpringWebfluxTelemetryBuilder setEmitExperimentalHttpServerTelemetry( /** Sets custom client {@link SpanNameExtractor} via transform function. */ @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setClientSpanNameExtractor( - Function, ? extends SpanNameExtractor> + Function< + SpanNameExtractor, + ? extends SpanNameExtractor> clientSpanNameExtractor) { clientBuilder.setSpanNameExtractor(clientSpanNameExtractor); return this; @@ -179,7 +181,7 @@ public SpringWebfluxTelemetryBuilder setClientSpanNameExtractor( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setServerSpanNameExtractor( Function< - SpanNameExtractor, + SpanNameExtractor, ? extends SpanNameExtractor> serverSpanNameExtractor) { serverBuilder.setSpanNameExtractor(serverSpanNameExtractor); diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java index 588263aff6e6..4acb087b72b5 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java @@ -74,7 +74,7 @@ public SpringWebMvcTelemetryBuilder setCapturedResponseHeaders(List resp @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setSpanNameExtractor( Function< - SpanNameExtractor, + SpanNameExtractor, ? extends SpanNameExtractor> spanNameExtractor) { builder.setSpanNameExtractor(spanNameExtractor); diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java index 7978664dd053..abf49f90fd25 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java @@ -73,7 +73,7 @@ public SpringWebMvcTelemetryBuilder setCapturedResponseHeaders(List resp @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setSpanNameExtractor( Function< - SpanNameExtractor, + SpanNameExtractor, ? extends SpanNameExtractor> spanNameExtractor) { builder.setSpanNameExtractor(spanNameExtractor); diff --git a/instrumentation/tapestry-5.4/javaagent/src/test/java/TapestryTest.java b/instrumentation/tapestry-5.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestryTest.java similarity index 98% rename from instrumentation/tapestry-5.4/javaagent/src/test/java/TapestryTest.java rename to instrumentation/tapestry-5.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestryTest.java index d6aadfd986b9..05d4d0209ee5 100644 --- a/instrumentation/tapestry-5.4/javaagent/src/test/java/TapestryTest.java +++ b/instrumentation/tapestry-5.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestryTest.java @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.javaagent.instrumentation.tapestry; + import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.SpanKind; diff --git a/instrumentation/vaadin-14.2/javaagent/build.gradle.kts b/instrumentation/vaadin-14.2/javaagent/build.gradle.kts index c4a2283a4649..dff55a64a534 100644 --- a/instrumentation/vaadin-14.2/javaagent/build.gradle.kts +++ b/instrumentation/vaadin-14.2/javaagent/build.gradle.kts @@ -59,7 +59,8 @@ testing { val vaadin14LatestTest by registering(JvmTestSuite::class) { dependencies { implementation(project(":instrumentation:vaadin-14.2:testing")) - implementation("com.vaadin:vaadin-spring-boot-starter:14.+") + // 14.12 requires license + implementation("com.vaadin:vaadin-spring-boot-starter:14.11.+") } } diff --git a/instrumentation/vaadin-14.2/javaagent/src/vaadin16Test/java/io/opentelemetry/javaagent/instrumentation/vaadin/Vaadin16Test.java b/instrumentation/vaadin-14.2/javaagent/src/vaadin16Test/java/io/opentelemetry/javaagent/instrumentation/vaadin/Vaadin16Test.java index 7c9ea5ac1e9a..f51f621c2c60 100644 --- a/instrumentation/vaadin-14.2/javaagent/src/vaadin16Test/java/io/opentelemetry/javaagent/instrumentation/vaadin/Vaadin16Test.java +++ b/instrumentation/vaadin-14.2/javaagent/src/vaadin16Test/java/io/opentelemetry/javaagent/instrumentation/vaadin/Vaadin16Test.java @@ -5,11 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.vaadin; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.awaitility.Awaitility.await; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.assertj.TracesAssert; import io.opentelemetry.sdk.trace.data.SpanData; import java.io.File; @@ -40,32 +39,32 @@ void assertFirstRequest() { assertThat(trace.get(0)) .satisfies( spans -> - OpenTelemetryAssertions.assertThat(spans.get(0)) + assertThat(spans.get(0)) .hasName("GET IndexHtmlRequestHandler.handleRequest") .hasNoParent() .hasKind(SpanKind.SERVER)); assertThat(trace) .anySatisfy( spans -> { - OpenTelemetryAssertions.assertThat(spans.get(0)) + assertThat(spans.get(0)) .hasName("POST " + getContextPath() + "/main") .hasNoParent() .hasKind(SpanKind.SERVER); - OpenTelemetryAssertions.assertThat(spans.get(1)) + assertThat(spans.get(1)) .hasName("SpringVaadinServletService.handleRequest") .hasParent(spans.get(0)) .hasKind(SpanKind.INTERNAL); // we don't assert all the handler spans as these vary between // vaadin versions - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 3)) + assertThat(spans.get(spans.size() - 3)) .hasName("UidlRequestHandler.handleRequest") .hasParent(spans.get(1)) .hasKind(SpanKind.INTERNAL); - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 2)) + assertThat(spans.get(spans.size() - 2)) .hasName("PublishedServerEventHandlerRpcHandler.handle") .hasParent(spans.get(spans.size() - 3)) .hasKind(SpanKind.INTERNAL); - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 1)) + assertThat(spans.get(spans.size() - 1)) .hasName("JavaScriptBootstrapUI.connectClient") .hasParent(spans.get(spans.size() - 2)) .hasKind(SpanKind.INTERNAL); diff --git a/instrumentation/vaadin-14.2/javaagent/src/vaadinLatestTest/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinLatestTest.java b/instrumentation/vaadin-14.2/javaagent/src/vaadinLatestTest/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinLatestTest.java index e92817a3acba..ab3fcd05974c 100644 --- a/instrumentation/vaadin-14.2/javaagent/src/vaadinLatestTest/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinLatestTest.java +++ b/instrumentation/vaadin-14.2/javaagent/src/vaadinLatestTest/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinLatestTest.java @@ -5,11 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.vaadin; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.awaitility.Awaitility.await; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.assertj.TracesAssert; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.List; @@ -28,32 +27,32 @@ void assertFirstRequest() { assertThat(trace.get(0)) .satisfies( spans -> - OpenTelemetryAssertions.assertThat(spans.get(0)) + assertThat(spans.get(0)) .hasName("GET IndexHtmlRequestHandler.handleRequest") .hasNoParent() .hasKind(SpanKind.SERVER)); assertThat(trace) .anySatisfy( spans -> { - OpenTelemetryAssertions.assertThat(spans.get(0)) + assertThat(spans.get(0)) .hasName("POST " + getContextPath()) .hasNoParent() .hasKind(SpanKind.SERVER); - OpenTelemetryAssertions.assertThat(spans.get(1)) + assertThat(spans.get(1)) .hasName("SpringVaadinServletService.handleRequest") .hasParent(spans.get(0)) .hasKind(SpanKind.INTERNAL); // we don't assert all the handler spans as these vary between // vaadin versions - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 3)) + assertThat(spans.get(spans.size() - 3)) .hasName("UidlRequestHandler.handleRequest") .hasParent(spans.get(1)) .hasKind(SpanKind.INTERNAL); - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 2)) + assertThat(spans.get(spans.size() - 2)) .hasName("PublishedServerEventHandlerRpcHandler.handle") .hasParent(spans.get(spans.size() - 3)) .hasKind(SpanKind.INTERNAL); - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 1)) + assertThat(spans.get(spans.size() - 1)) .hasName("UI.connectClient") .hasParent(spans.get(spans.size() - 2)) .hasKind(SpanKind.INTERNAL); diff --git a/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadin14Test.java b/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadin14Test.java index fff1388fa74f..82eb7ee48dd8 100644 --- a/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadin14Test.java +++ b/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadin14Test.java @@ -5,11 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.vaadin; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.awaitility.Awaitility.await; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.List; @@ -23,17 +22,17 @@ void assertFirstRequest() { assertThat(traces.get(0)) .satisfies( spans -> { - OpenTelemetryAssertions.assertThat(spans.get(0)) + assertThat(spans.get(0)) .hasName("GET " + getContextPath() + "/main") .hasNoParent() .hasKind(SpanKind.SERVER); - OpenTelemetryAssertions.assertThat(spans.get(1)) + assertThat(spans.get(1)) .hasName("SpringVaadinServletService.handleRequest") .hasParent(spans.get(0)) .hasKind(SpanKind.INTERNAL); // we don't assert all the handler spans as these vary between // vaadin versions - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 1)) + assertThat(spans.get(spans.size() - 1)) .hasName("BootstrapHandler.handleRequest") .hasParent(spans.get(1)) .hasKind(SpanKind.INTERNAL); diff --git a/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java b/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java index 161a574119ed..f80635f5cae2 100644 --- a/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java +++ b/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.vaadin; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.awaitility.Awaitility.await; import com.vaadin.flow.server.Version; @@ -14,7 +14,6 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerUsingTest; import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.trace.data.SpanData; import java.io.File; import java.io.IOException; @@ -154,21 +153,21 @@ private void assertButtonClick() { assertThat(traces.get(0)) .satisfies( spans -> { - OpenTelemetryAssertions.assertThat(spans.get(0)) + assertThat(spans.get(0)) .hasName("POST " + getContextPath() + "/main") .hasNoParent() .hasKind(SpanKind.SERVER); - OpenTelemetryAssertions.assertThat(spans.get(1)) + assertThat(spans.get(1)) .hasName("SpringVaadinServletService.handleRequest") .hasParent(spans.get(0)) .hasKind(SpanKind.INTERNAL); // we don't assert all the handler spans as these vary between // vaadin versions - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 2)) + assertThat(spans.get(spans.size() - 2)) .hasName("UidlRequestHandler.handleRequest") .hasParent(spans.get(1)) .hasKind(SpanKind.INTERNAL); - OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 1)) + assertThat(spans.get(spans.size() - 1)) .hasName("EventRpcHandler.handle/click") .hasParent(spans.get(spans.size() - 2)) .hasKind(SpanKind.INTERNAL); diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentInitializer.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentInitializer.java index 5410c883c722..adb7668fa79b 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentInitializer.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentInitializer.java @@ -26,6 +26,7 @@ public final class AgentInitializer { @Nullable private static ClassLoader agentClassLoader = null; @Nullable private static AgentStarter agentStarter = null; private static boolean isSecurityManagerSupportEnabled = false; + private static volatile boolean agentStarted = false; public static void initialize(Instrumentation inst, File javaagentFile, boolean fromPremain) throws Exception { @@ -51,6 +52,7 @@ public Void run() throws Exception { agentStarter = createAgentStarter(agentClassLoader, inst, javaagentFile); if (!fromPremain || !delayAgentStart()) { agentStarter.start(); + agentStarted = true; } return null; } @@ -149,11 +151,27 @@ public static void delayedStartHook() throws Exception { @Override public Void run() { agentStarter.start(); + agentStarted = true; return null; } }); } + /** + * Check whether agent has started or not along with VM. + * + *

This method is used by + * io.opentelemetry.javaagent.tooling.AgentStarterImpl#InetAddressClassFileTransformer internally + * to check whether agent has started. + * + * @param vmStarted flag about whether VM has started or not. + * @return {@code true} if agent has started or not along with VM, {@code false} otherwise. + */ + @SuppressWarnings("unused") + public static boolean isAgentStarted(boolean vmStarted) { + return vmStarted && agentStarted; + } + public static ClassLoader getExtensionsClassLoader() { // agentStarter can be null when running tests return agentStarter != null ? agentStarter.getExtensionClassLoader() : null; diff --git a/javaagent-tooling/jdk18-testing/build.gradle.kts b/javaagent-tooling/jdk18-testing/build.gradle.kts new file mode 100644 index 000000000000..ab1bf712d97f --- /dev/null +++ b/javaagent-tooling/jdk18-testing/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + compileOnly("io.opentelemetry:opentelemetry-sdk-common") +} + +otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_18) +} diff --git a/javaagent-tooling/jdk18-testing/src/main/java/testing/TestResourceProvider.java b/javaagent-tooling/jdk18-testing/src/main/java/testing/TestResourceProvider.java new file mode 100644 index 000000000000..9db4d5df5006 --- /dev/null +++ b/javaagent-tooling/jdk18-testing/src/main/java/testing/TestResourceProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package testing; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; +import java.net.InetAddress; +import java.net.UnknownHostException; + +@AutoService(ResourceProvider.class) +public class TestResourceProvider implements ResourceProvider { + + @Override + public Resource createResource(ConfigProperties config) { + // used in test to determine whether this method was called + System.setProperty("test.resource.provider.called", "true"); + // this call trigger loading InetAddressResolverProvider SPI on jdk 18 + try { + InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + throw new IllegalStateException(e); + } + return Resource.empty(); + } +} diff --git a/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/InetAddressResolverTest.java b/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/InetAddressResolverTest.java new file mode 100644 index 000000000000..f21e494b5f0a --- /dev/null +++ b/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/InetAddressResolverTest.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.inetaddress; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.InetAddress; +import org.junit.jupiter.api.Test; + +public class InetAddressResolverTest { + + @Test + void agentStartShouldNotTriggerLoadingCustomInetAddressResolvers() throws Exception { + // This system property is set in TestResourceProvider + assertThat(System.getProperty("test.resource.provider.called")).isEqualTo("true"); + // Agent start should not trigger loading (and instantiating) custom InetAddress resolvers + assertThat(TestAddressResolver.isInstantiated()).isFalse(); + + // Trigger loading (and instantiating) custom InetAddress resolvers manually + InetAddress.getAllByName("test"); + + // Verify that custom InetAddress resolver loaded and instantiated + assertThat(TestAddressResolver.isInstantiated()).isTrue(); + } +} diff --git a/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/TestAddressResolver.java b/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/TestAddressResolver.java new file mode 100644 index 000000000000..9c0df5aad70b --- /dev/null +++ b/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/TestAddressResolver.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.inetaddress; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.net.spi.InetAddressResolver; +import java.util.stream.Stream; + +public class TestAddressResolver implements InetAddressResolver { + + private static volatile boolean instantiated = false; + + @SuppressWarnings("StaticAssignmentInConstructor") + public TestAddressResolver() { + TestAddressResolver.instantiated = true; + } + + public static boolean isInstantiated() { + return instantiated; + } + + @Override + public Stream lookupByName(String host, LookupPolicy lookupPolicy) + throws UnknownHostException { + if (host.equals("test")) { + return Stream.of(InetAddress.getByAddress(new byte[] {127, 0, 0, 1})); + } + throw new UnknownHostException(); + } + + @Override + public String lookupByAddress(byte[] addr) { + throw new UnsupportedOperationException(); + } +} diff --git a/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/TestAddressResolverProvider.java b/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/TestAddressResolverProvider.java new file mode 100644 index 000000000000..f1af2597e5c0 --- /dev/null +++ b/javaagent-tooling/jdk18-testing/src/test/java/io/opentelemetry/javaagent/tooling/inetaddress/TestAddressResolverProvider.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.inetaddress; + +import java.net.spi.InetAddressResolver; +import java.net.spi.InetAddressResolverProvider; + +public class TestAddressResolverProvider extends InetAddressResolverProvider { + + @Override + public InetAddressResolver get(Configuration configuration) { + return new TestAddressResolver(); + } + + @Override + public String name() { + return "Test Internet Address Resolver Provider"; + } +} diff --git a/javaagent-tooling/jdk18-testing/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider b/javaagent-tooling/jdk18-testing/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider new file mode 100644 index 000000000000..c4d2e7912b78 --- /dev/null +++ b/javaagent-tooling/jdk18-testing/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider @@ -0,0 +1 @@ +io.opentelemetry.javaagent.tooling.inetaddress.TestAddressResolverProvider diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java index 7f12889be6b7..ab5cb93c22ed 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java @@ -68,6 +68,8 @@ public boolean delayStart() { @Override public void start() { + installTransformers(); + EarlyInitAgentConfig earlyConfig = EarlyInitAgentConfig.create(); extensionClassLoader = createExtensionClassLoader(getClass().getClassLoader(), earlyConfig); @@ -115,6 +117,14 @@ public void start() { } } + private void installTransformers() { + // prevents loading InetAddressResolverProvider SPI before agent has started + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/7130 + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/10921 + InetAddressClassFileTransformer transformer = new InetAddressClassFileTransformer(); + instrumentation.addTransformer(transformer, true); + } + @SuppressWarnings("SystemOut") private static void logUnrecognizedLoggerImplWarning(String loggerImplementationName) { System.err.println( @@ -180,4 +190,60 @@ public void visitCode() { return hookInserted ? cw.toByteArray() : null; } } + + private static class InetAddressClassFileTransformer implements ClassFileTransformer { + boolean hookInserted = false; + + @Override + public byte[] transform( + ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) { + if (!"java/net/InetAddress".equals(className)) { + return null; + } + ClassReader cr = new ClassReader(classfileBuffer); + ClassWriter cw = new ClassWriter(cr, 0); + ClassVisitor cv = + new ClassVisitor(AsmApi.VERSION, cw) { + @Override + public MethodVisitor visitMethod( + int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (!"resolver".equals(name)) { + return mv; + } + return new MethodVisitor(api, mv) { + @Override + public void visitMethodInsn( + int opcode, + String ownerClassName, + String methodName, + String descriptor, + boolean isInterface) { + super.visitMethodInsn( + opcode, ownerClassName, methodName, descriptor, isInterface); + // rewrite Vm.isBooted() to AgentInitializer.isAgentStarted(Vm.isBooted()) + if ("jdk/internal/misc/VM".equals(ownerClassName) + && "isBooted".equals(methodName)) { + super.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName(AgentInitializer.class), + "isAgentStarted", + "(Z)Z", + false); + hookInserted = true; + } + } + }; + } + }; + + cr.accept(cv, 0); + + return hookInserted ? cw.toByteArray() : null; + } + } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/TypeTransformerImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/TypeTransformerImpl.java index 1382acc66e2d..e3b6285d334b 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/TypeTransformerImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/TypeTransformerImpl.java @@ -8,6 +8,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.tooling.Utils; import io.opentelemetry.javaagent.tooling.bytebuddy.ExceptionHandlers; +import io.opentelemetry.javaagent.tooling.instrumentation.indy.ForceDynamicallyTypedAssignReturnedFactory; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -21,7 +22,9 @@ final class TypeTransformerImpl implements TypeTransformer { this.agentBuilder = agentBuilder; adviceMapping = Advice.withCustomMapping() - .with(new Advice.AssignReturned.Factory().withSuppressed(Throwable.class)); + .with( + new ForceDynamicallyTypedAssignReturnedFactory( + new Advice.AssignReturned.Factory().withSuppressed(Throwable.class))); } @Override diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/ForceDynamicallyTypedAssignReturnedFactory.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/ForceDynamicallyTypedAssignReturnedFactory.java new file mode 100644 index 000000000000..dd0c88bb4752 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/ForceDynamicallyTypedAssignReturnedFactory.java @@ -0,0 +1,136 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.instrumentation.indy; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.annotation.AnnotationValue; +import net.bytebuddy.description.enumeration.EnumerationDescription; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.matcher.ElementMatchers; + +/** + * This factory is designed to wrap around {@link Advice.PostProcessor.Factory} and ensures that + * {@link net.bytebuddy.implementation.bytecode.assign.Assigner.Typing#DYNAMIC} is used everywhere. + * + *

This helps by avoiding errors where the instrumented bytecode is suddenly unloadable due to + * incompatible assignments and preventing cluttering advice code annotations with the explicit + * typing. + */ +public class ForceDynamicallyTypedAssignReturnedFactory implements Advice.PostProcessor.Factory { + + private static final String TO_ARGUMENTS_TYPENAME = + Advice.AssignReturned.ToArguments.class.getName(); + private static final String TO_ARGUMENT_TYPENAME = + Advice.AssignReturned.ToArguments.ToArgument.class.getName(); + private static final String TO_ALL_ARGUMENTS_TYPENAME = + Advice.AssignReturned.ToAllArguments.class.getName(); + private static final String TO_THIS_TYPENAME = Advice.AssignReturned.ToThis.class.getName(); + private static final String TO_FIELDS_TYPENAME = Advice.AssignReturned.ToFields.class.getName(); + private static final String TO_FIELD_TYPENAME = + Advice.AssignReturned.ToFields.ToField.class.getName(); + private static final String TO_RETURNED_TYPENAME = + Advice.AssignReturned.ToReturned.class.getName(); + private static final String TO_THROWN_TYPENAME = Advice.AssignReturned.ToThrown.class.getName(); + private static final EnumerationDescription DYNAMIC_TYPING = + new EnumerationDescription.ForLoadedEnumeration(Assigner.Typing.DYNAMIC); + + private final Advice.PostProcessor.Factory delegate; + + public ForceDynamicallyTypedAssignReturnedFactory(Advice.PostProcessor.Factory delegate) { + this.delegate = delegate; + } + + @Override + public Advice.PostProcessor make(MethodDescription.InDefinedShape adviceMethod, boolean exit) { + return delegate.make(forceDynamicTyping(adviceMethod), exit); + } + + // Visible for testing + static MethodDescription.InDefinedShape forceDynamicTyping( + MethodDescription.InDefinedShape adviceMethod) { + return new MethodDescription.Latent( + adviceMethod.getDeclaringType(), + adviceMethod.getInternalName(), + adviceMethod.getModifiers(), + adviceMethod.getTypeVariables().asTokenList(ElementMatchers.none()), + adviceMethod.getReturnType(), + adviceMethod.getParameters().asTokenList(ElementMatchers.none()), + adviceMethod.getExceptionTypes(), + forceDynamicTyping(adviceMethod.getDeclaredAnnotations()), + adviceMethod.getDefaultValue(), + adviceMethod.getReceiverType()); + } + + private static List forceDynamicTyping( + List declaredAnnotations) { + return declaredAnnotations.stream() + .map(ForceDynamicallyTypedAssignReturnedFactory::forceDynamicTyping) + .collect(Collectors.toList()); + } + + private static AnnotationDescription forceDynamicTyping(AnnotationDescription anno) { + + String name = anno.getAnnotationType().getName(); + if (name.equals(TO_FIELD_TYPENAME) + || name.equals(TO_ARGUMENT_TYPENAME) + || name.equals(TO_THIS_TYPENAME) + || name.equals(TO_ALL_ARGUMENTS_TYPENAME) + || name.equals(TO_RETURNED_TYPENAME) + || name.equals(TO_THROWN_TYPENAME)) { + return replaceAnnotationValue( + anno, "typing", oldVal -> AnnotationValue.ForEnumerationDescription.of(DYNAMIC_TYPING)); + } else if (name.equals(TO_FIELDS_TYPENAME) || name.equals(TO_ARGUMENTS_TYPENAME)) { + return replaceAnnotationValue( + anno, + "value", + oldVal -> { + if (!oldVal.getState().isDefined()) { + return null; + } + AnnotationDescription[] resolve = (AnnotationDescription[]) oldVal.resolve(); + if (resolve.length == 0) { + return oldVal; + } + AnnotationDescription[] newValueList = + Arrays.stream(resolve) + .map(ForceDynamicallyTypedAssignReturnedFactory::forceDynamicTyping) + .toArray(AnnotationDescription[]::new); + TypeDescription subType = newValueList[0].getAnnotationType(); + return AnnotationValue.ForDescriptionArray.of(subType, newValueList); + }); + } + return anno; + } + + private static AnnotationDescription replaceAnnotationValue( + AnnotationDescription anno, + String propertyName, + Function, AnnotationValue> valueMapper) { + AnnotationValue oldValue = anno.getValue(propertyName); + AnnotationValue newValue = valueMapper.apply(oldValue); + Map> updatedValues = new HashMap<>(); + for (MethodDescription.InDefinedShape property : + anno.getAnnotationType().getDeclaredMethods()) { + AnnotationValue value = anno.getValue(property); + if (!propertyName.equals(property.getName()) && value.getState().isDefined()) { + updatedValues.put(property.getName(), value); + } + } + if (newValue != null) { + updatedValues.put(propertyName, newValue); + } + return new AnnotationDescription.Latent(anno.getAnnotationType(), updatedValues) {}; + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyTypeTransformerImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyTypeTransformerImpl.java index e78ab79236fd..df7f67ded219 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyTypeTransformerImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyTypeTransformerImpl.java @@ -30,7 +30,9 @@ public IndyTypeTransformerImpl( this.instrumentationModule = module; this.adviceMapping = Advice.withCustomMapping() - .with(new Advice.AssignReturned.Factory().withSuppressed(Throwable.class)) + .with( + new ForceDynamicallyTypedAssignReturnedFactory( + new Advice.AssignReturned.Factory().withSuppressed(Throwable.class))) .bootstrap( IndyBootstrap.getIndyBootstrapMethod(), IndyBootstrap.getAdviceBootstrapArguments(instrumentationModule)); diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/ForceDynamicallyTypedAssignReturnedFactoryTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/ForceDynamicallyTypedAssignReturnedFactoryTest.java new file mode 100644 index 000000000000..ccba634f03ae --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/ForceDynamicallyTypedAssignReturnedFactoryTest.java @@ -0,0 +1,113 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.instrumentation.indy; + +import static org.assertj.core.api.Assertions.assertThat; + +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; +import net.bytebuddy.asm.Advice.AssignReturned.ToFields.ToField; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import org.junit.jupiter.api.Test; + +public class ForceDynamicallyTypedAssignReturnedFactoryTest { + + @AssignReturned.ToFields(@ToField(value = "foo", index = 42)) + @AssignReturned.ToArguments(@ToArgument(value = 3, index = 7)) + @AssignReturned.ToReturned(index = 4) + @AssignReturned.ToThrown(index = 5) + @AssignReturned.ToThis(index = 6) + @AssignReturned.ToAllArguments(index = 7) + @Advice.OnMethodEnter + static void testMethod() {} + + @Test + public void checkTypingMadeDynamic() { + MethodDescription.InDefinedShape original = + TypeDescription.ForLoadedType.of(ForceDynamicallyTypedAssignReturnedFactoryTest.class) + .getDeclaredMethods() + .stream() + .filter(method -> method.getName().equals("testMethod")) + .findFirst() + .get(); + + ClassLoader cl = ForceDynamicallyTypedAssignReturnedFactoryTest.class.getClassLoader(); + + MethodDescription modified = + ForceDynamicallyTypedAssignReturnedFactory.forceDynamicTyping(original); + assertThat(modified.getDeclaredAnnotations()) + .hasSize(7) + .anySatisfy( + toFields -> { + assertThat(toFields.getAnnotationType().getName()) + .isEqualTo(AssignReturned.ToFields.class.getName()); + assertThat((AnnotationDescription[]) toFields.getValue("value").resolve()) + .hasSize(1) + .anySatisfy( + toField -> { + assertThat(toField.getValue("value").resolve()).isEqualTo("foo"); + assertThat(toField.getValue("index").resolve()).isEqualTo(42); + assertThat(toField.getValue("typing").load(cl).resolve()) + .isEqualTo(Assigner.Typing.DYNAMIC); + }); + }) + .anySatisfy( + toArguments -> { + assertThat(toArguments.getAnnotationType().getName()) + .isEqualTo(AssignReturned.ToArguments.class.getName()); + assertThat((AnnotationDescription[]) toArguments.getValue("value").resolve()) + .hasSize(1) + .anySatisfy( + toArgument -> { + assertThat(toArgument.getValue("value").resolve()).isEqualTo(3); + assertThat(toArgument.getValue("index").resolve()).isEqualTo(7); + assertThat(toArgument.getValue("typing").load(cl).resolve()) + .isEqualTo(Assigner.Typing.DYNAMIC); + }); + }) + .anySatisfy( + toReturned -> { + assertThat(toReturned.getAnnotationType().getName()) + .isEqualTo(AssignReturned.ToReturned.class.getName()); + assertThat(toReturned.getValue("index").resolve()).isEqualTo(4); + assertThat(toReturned.getValue("typing").load(cl).resolve()) + .isEqualTo(Assigner.Typing.DYNAMIC); + }) + .anySatisfy( + toThrown -> { + assertThat(toThrown.getAnnotationType().getName()) + .isEqualTo(AssignReturned.ToThrown.class.getName()); + assertThat(toThrown.getValue("index").resolve()).isEqualTo(5); + assertThat(toThrown.getValue("typing").load(cl).resolve()) + .isEqualTo(Assigner.Typing.DYNAMIC); + }) + .anySatisfy( + toThis -> { + assertThat(toThis.getAnnotationType().getName()) + .isEqualTo(AssignReturned.ToThis.class.getName()); + assertThat(toThis.getValue("index").resolve()).isEqualTo(6); + assertThat(toThis.getValue("typing").load(cl).resolve()) + .isEqualTo(Assigner.Typing.DYNAMIC); + }) + .anySatisfy( + toAllArguments -> { + assertThat(toAllArguments.getAnnotationType().getName()) + .isEqualTo(AssignReturned.ToAllArguments.class.getName()); + assertThat(toAllArguments.getValue("index").resolve()).isEqualTo(7); + assertThat(toAllArguments.getValue("typing").load(cl).resolve()) + .isEqualTo(Assigner.Typing.DYNAMIC); + }) + .anySatisfy( + onMethodEnter -> { + assertThat(onMethodEnter.getAnnotationType().getName()) + .isEqualTo(Advice.OnMethodEnter.class.getName()); + }); + } +} diff --git a/licenses/byte-buddy-dep-1.14.18.jar/META-INF/LICENSE b/licenses/byte-buddy-dep-1.15.1.jar/META-INF/LICENSE similarity index 100% rename from licenses/byte-buddy-dep-1.14.18.jar/META-INF/LICENSE rename to licenses/byte-buddy-dep-1.15.1.jar/META-INF/LICENSE diff --git a/licenses/byte-buddy-dep-1.14.18.jar/META-INF/NOTICE b/licenses/byte-buddy-dep-1.15.1.jar/META-INF/NOTICE similarity index 100% rename from licenses/byte-buddy-dep-1.14.18.jar/META-INF/NOTICE rename to licenses/byte-buddy-dep-1.15.1.jar/META-INF/NOTICE diff --git a/licenses/licenses.md b/licenses/licenses.md index fe413be6f62e..8d979cb97ee1 100644 --- a/licenses/licenses.md +++ b/licenses/licenses.md @@ -59,107 +59,107 @@ > - **POM Project URL**: [https://github.com/square/okio/](https://github.com/square/okio/) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**11** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api` **Version:** `1.40.0` +**11** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**12** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api-incubator` **Version:** `1.40.0-alpha` +**12** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api-incubator` **Version:** `1.41.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**13** **Group:** `io.opentelemetry` **Name:** `opentelemetry-context` **Version:** `1.40.0` +**13** **Group:** `io.opentelemetry` **Name:** `opentelemetry-context` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**14** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-common` **Version:** `1.40.0` +**14** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-common` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**15** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging` **Version:** `1.40.0` +**15** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**16** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging-otlp` **Version:** `1.40.0` +**16** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging-otlp` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**17** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp` **Version:** `1.40.0` +**17** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**18** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-common` **Version:** `1.40.0` +**18** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-common` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**19** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-prometheus` **Version:** `1.40.0-alpha` +**19** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-prometheus` **Version:** `1.41.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**20** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-sender-okhttp` **Version:** `1.40.0` +**20** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-sender-okhttp` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**21** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-zipkin` **Version:** `1.40.0` +**21** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-zipkin` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**22** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-kotlin` **Version:** `1.40.0` +**22** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-kotlin` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**23** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-trace-propagators` **Version:** `1.40.0` +**23** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-trace-propagators` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**24** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk` **Version:** `1.40.0` +**24** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**25** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-common` **Version:** `1.40.0` +**25** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-common` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**26** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure` **Version:** `1.40.0` +**26** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**27** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure-spi` **Version:** `1.40.0` +**27** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure-spi` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**28** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-incubator` **Version:** `1.40.0-alpha` +**28** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-incubator` **Version:** `1.41.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**29** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-jaeger-remote-sampler` **Version:** `1.40.0` +**29** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-jaeger-remote-sampler` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**30** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-logs` **Version:** `1.40.0` +**30** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-logs` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**31** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-metrics` **Version:** `1.40.0` +**31** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-metrics` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**32** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-trace` **Version:** `1.40.0` +**32** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-trace` **Version:** `1.41.0` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**33** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-resources` **Version:** `1.37.0-alpha` +**33** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-resources` **Version:** `1.38.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**34** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-xray-propagator` **Version:** `1.37.0-alpha` +**34** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-xray-propagator` **Version:** `1.38.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**35** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-baggage-processor` **Version:** `1.37.0-alpha` +**35** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-baggage-processor` **Version:** `1.38.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**36** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-gcp-resources` **Version:** `1.37.0-alpha` +**36** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-gcp-resources` **Version:** `1.38.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) @@ -212,24 +212,24 @@ > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) > - **Embedded license files**: [zipkin-2.27.1.jar/META-INF/LICENSE](zipkin-2.27.1.jar/META-INF/LICENSE) -**48** **Group:** `net.bytebuddy` **Name:** `byte-buddy-dep` **Version:** `1.14.18` +**48** **Group:** `net.bytebuddy` **Name:** `byte-buddy-dep` **Version:** `1.15.1` > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -> - **Embedded license files**: [byte-buddy-dep-1.14.18.jar/META-INF/LICENSE](byte-buddy-dep-1.14.18.jar/META-INF/LICENSE) - - [byte-buddy-dep-1.14.18.jar/META-INF/NOTICE](byte-buddy-dep-1.14.18.jar/META-INF/NOTICE) +> - **Embedded license files**: [byte-buddy-dep-1.15.1.jar/META-INF/LICENSE](byte-buddy-dep-1.15.1.jar/META-INF/LICENSE) + - [byte-buddy-dep-1.15.1.jar/META-INF/NOTICE](byte-buddy-dep-1.15.1.jar/META-INF/NOTICE) **49** **Group:** `org.jetbrains` **Name:** `annotations` **Version:** `13.0` > - **POM Project URL**: [http://www.jetbrains.org](http://www.jetbrains.org) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**50** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib` **Version:** `2.0.0` +**50** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib` **Version:** `2.0.10` > - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**51** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-jdk7` **Version:** `2.0.0` +**51** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-jdk7` **Version:** `2.0.10` > - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**52** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-jdk8` **Version:** `2.0.0` +**52** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-jdk8` **Version:** `2.0.10` > - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) diff --git a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java index 04cd424be6f1..d9d49612c18a 100644 --- a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java +++ b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java @@ -8,6 +8,7 @@ import static io.opentelemetry.javaagent.tooling.muzzle.references.Flag.MinimumVisibilityFlag.PACKAGE_OR_HIGHER; import static io.opentelemetry.javaagent.tooling.muzzle.references.Flag.MinimumVisibilityFlag.PROTECTED_OR_HIGHER; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.entry; import external.instrumentation.ExternalHelper; @@ -31,7 +32,6 @@ import muzzle.TestClasses.MethodBodyAdvice; import muzzle.TestClasses.Nested; import muzzle.other.OtherTestClasses; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -369,7 +369,7 @@ public void shouldNotCollectVirtualFieldsForInvalidScenario( @SuppressWarnings("unused") String desc, String adviceClassName) { ReferenceCollector collector = new ReferenceCollector(s -> false); - Assertions.assertThatExceptionOfType(MuzzleCompilationException.class) + assertThatExceptionOfType(MuzzleCompilationException.class) .isThrownBy( () -> { collector.collectReferencesFromAdvice(adviceClassName); diff --git a/settings.gradle.kts b/settings.gradle.kts index e728bee40217..b5a17c333e59 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,10 +1,10 @@ pluginManagement { plugins { - id("com.github.jk1.dependency-license-report") version "2.8" + id("com.github.jk1.dependency-license-report") version "2.9" id("com.google.cloud.tools.jib") version "3.4.3" - id("com.gradle.plugin-publish") version "1.2.1" + id("com.gradle.plugin-publish") version "1.2.2" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" - id("org.jetbrains.kotlin.jvm") version "2.0.10" + id("org.jetbrains.kotlin.jvm") version "2.0.20" id("org.xbib.gradle.plugin.jflex") version "3.0.2" id("org.unbroken-dome.xjc") version "2.0.0" id("org.graalvm.buildtools.native") version "0.10.2" @@ -12,7 +12,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "3.17.6" + id("com.gradle.develocity") version "3.18" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" // this can't live in pluginManagement currently due to @@ -121,6 +121,7 @@ include(":javaagent-bootstrap") include(":javaagent-extension-api") include(":javaagent-tooling") include(":javaagent-tooling:javaagent-tooling-java9") +include(":javaagent-tooling:jdk18-testing") include(":javaagent-internal-logging-application") include(":javaagent-internal-logging-simple") include(":javaagent") @@ -332,14 +333,15 @@ include(":instrumentation:jaxws:jaxws-2.0:javaagent") include(":instrumentation:jaxws:jaxws-2.0-arquillian-testing") include(":instrumentation:jaxws:jaxws-2.0-axis2-1.6:javaagent") include(":instrumentation:jaxws:jaxws-2.0-common-testing") -include(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent") -include(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent-unit-tests") include(":instrumentation:jaxws:jaxws-2.0-metro-2.2-testing") include(":instrumentation:jaxws:jaxws-2.0-tomee-testing") include(":instrumentation:jaxws:jaxws-2.0-wildfly-testing") include(":instrumentation:jaxws:jaxws-3.0-common-testing") +include(":instrumentation:jaxws:jaxws-3.0-cxf-4.0-testing") include(":instrumentation:jaxws:jaxws-3.0-metro-2.2-testing") include(":instrumentation:jaxws:jaxws-common:javaagent") +include(":instrumentation:jaxws:jaxws-cxf-3.0:javaagent") +include(":instrumentation:jaxws:jaxws-cxf-3.0:javaagent-unit-tests") include(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent") include(":instrumentation:jaxws:jaxws-metro-2.2:javaagent") include(":instrumentation:jboss-logmanager:jboss-logmanager-appender-1.1:javaagent") diff --git a/smoke-tests/images/quarkus/build.gradle.kts b/smoke-tests/images/quarkus/build.gradle.kts index 1951fad7a751..2ef99a41da97 100644 --- a/smoke-tests/images/quarkus/build.gradle.kts +++ b/smoke-tests/images/quarkus/build.gradle.kts @@ -12,11 +12,11 @@ plugins { id("otel.java-conventions") id("com.google.cloud.tools.jib") - id("io.quarkus") version "3.13.2" + id("io.quarkus") version "3.14.1" } dependencies { - implementation(enforcedPlatform("io.quarkus:quarkus-bom:3.13.2")) + implementation(enforcedPlatform("io.quarkus:quarkus-bom:3.14.1")) implementation("io.quarkus:quarkus-resteasy") } diff --git a/smoke-tests/images/servlet/build.gradle.kts b/smoke-tests/images/servlet/build.gradle.kts index 8cf01a007e44..3614722c487b 100644 --- a/smoke-tests/images/servlet/build.gradle.kts +++ b/smoke-tests/images/servlet/build.gradle.kts @@ -1,6 +1,6 @@ import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage import com.bmuschko.gradle.docker.tasks.image.DockerPushImage -import org.gradle.configurationcache.extensions.capitalized +import org.apache.commons.lang.StringUtils plugins { id("otel.spotless-conventions") @@ -104,7 +104,7 @@ tasks { continue } println(server) - val serverName = server.capitalized() + val serverName = StringUtils.capitalize(server) for (entry in matrices) { for (version in entry.version) { val dotIndex = version.indexOf('.') diff --git a/smoke-tests/images/servlet/src/wildfly.dockerfile b/smoke-tests/images/servlet/src/wildfly.dockerfile index dd03cebd1e40..8831c9b4756b 100644 --- a/smoke-tests/images/servlet/src/wildfly.dockerfile +++ b/smoke-tests/images/servlet/src/wildfly.dockerfile @@ -8,7 +8,7 @@ ARG baseDownloadUrl # The user ID 1000 is the default for the first "regular" user on Fedora/RHEL, # so there is a high chance that this ID will be equal to the current user # making it easier to use volumes (no permission issues) -RUN groupadd -r jboss -g 1000 && useradd -u 1000 -r -g jboss -m -d /opt/jboss -s /sbin/nologin -c "JBoss user" jboss && \ +RUN groupadd -r jboss -g 1001 && useradd -u 1001 -r -g jboss -m -d /opt/jboss -s /sbin/nologin -c "JBoss user" jboss && \ chmod 755 /opt/jboss # Set the working directory to jboss' user home directory diff --git a/testing-common/integration-tests/src/main/java/io/opentelemetry/javaagent/IndyInstrumentationTestModule.java b/testing-common/integration-tests/src/main/java/io/opentelemetry/javaagent/IndyInstrumentationTestModule.java index 1206f0056825..c6424d8fea65 100644 --- a/testing-common/integration-tests/src/main/java/io/opentelemetry/javaagent/IndyInstrumentationTestModule.java +++ b/testing-common/integration-tests/src/main/java/io/opentelemetry/javaagent/IndyInstrumentationTestModule.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent; -import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC; import static net.bytebuddy.matcher.ElementMatchers.named; import com.google.auto.service.AutoService; @@ -92,7 +91,7 @@ public void transform(TypeTransformer transformer) { public static class AssignFieldViaReturnAdvice { @Advice.OnMethodEnter(inline = false) - @Advice.AssignReturned.ToFields(@ToField(value = "privateField", typing = DYNAMIC)) + @Advice.AssignReturned.ToFields(@ToField(value = "privateField")) public static String onEnter(@Advice.Argument(0) String toAssign) { return toAssign; } @@ -102,7 +101,7 @@ public static String onEnter(@Advice.Argument(0) String toAssign) { public static class AssignFieldViaArrayAdvice { @Advice.OnMethodEnter(inline = false) - @Advice.AssignReturned.ToFields(@ToField(value = "privateField", index = 1, typing = DYNAMIC)) + @Advice.AssignReturned.ToFields(@ToField(value = "privateField", index = 1)) public static Object[] onEnter(@Advice.Argument(0) String toAssign) { return new Object[] {"ignoreme", toAssign}; } @@ -112,7 +111,7 @@ public static Object[] onEnter(@Advice.Argument(0) String toAssign) { public static class AssignArgumentViaReturnAdvice { @Advice.OnMethodEnter(inline = false) - @Advice.AssignReturned.ToArguments(@ToArgument(value = 0, typing = DYNAMIC)) + @Advice.AssignReturned.ToArguments(@ToArgument(value = 0)) public static String onEnter(@Advice.Argument(1) String toAssign) { return toAssign; } @@ -122,7 +121,7 @@ public static String onEnter(@Advice.Argument(1) String toAssign) { public static class AssignArgumentViaArrayAdvice { @Advice.OnMethodEnter(inline = false) - @Advice.AssignReturned.ToArguments(@ToArgument(value = 0, index = 1, typing = DYNAMIC)) + @Advice.AssignReturned.ToArguments(@ToArgument(value = 0, index = 1)) public static Object[] onEnter(@Advice.Argument(1) String toAssign) { return new Object[] {"ignoreme", toAssign}; } @@ -132,7 +131,7 @@ public static Object[] onEnter(@Advice.Argument(1) String toAssign) { public static class AssignReturnViaReturnAdvice { @Advice.OnMethodExit(inline = false) - @Advice.AssignReturned.ToReturned(typing = DYNAMIC) + @Advice.AssignReturned.ToReturned public static String onExit(@Advice.Argument(0) String toAssign) { return toAssign; } @@ -142,7 +141,7 @@ public static String onExit(@Advice.Argument(0) String toAssign) { public static class AssignReturnViaArrayAdvice { @Advice.OnMethodExit(inline = false) - @Advice.AssignReturned.ToReturned(index = 1, typing = DYNAMIC) + @Advice.AssignReturned.ToReturned(index = 1) public static Object[] onExit(@Advice.Argument(0) String toAssign) { return new Object[] {"ignoreme", toAssign}; } @@ -152,7 +151,7 @@ public static Object[] onExit(@Advice.Argument(0) String toAssign) { public static class GetHelperClassAdvice { @Advice.OnMethodExit(inline = false) - @Advice.AssignReturned.ToReturned(typing = DYNAMIC) + @Advice.AssignReturned.ToReturned public static Class onExit(@Advice.Argument(0) boolean localHelper) { if (localHelper) { return LocalHelper.class; @@ -177,7 +176,7 @@ public static void onMethodEnter() { throw new RuntimeException("This exception should be suppressed"); } - @Advice.AssignReturned.ToReturned(typing = DYNAMIC) + @Advice.AssignReturned.ToReturned @Advice.OnMethodExit( suppress = Throwable.class, onThrowable = Throwable.class, @@ -194,7 +193,7 @@ public static LocalHelper onMethodEnter() { return new LocalHelper(); } - @Advice.AssignReturned.ToReturned(typing = DYNAMIC) + @Advice.AssignReturned.ToReturned @Advice.OnMethodExit( suppress = Throwable.class, onThrowable = Throwable.class, diff --git a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy index 3dd760176144..38449f0ff596 100644 --- a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy +++ b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy @@ -15,12 +15,12 @@ import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions import io.opentelemetry.sdk.trace.data.SpanData import spock.lang.Requires import spock.lang.Shared import spock.lang.Unroll +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat import static org.junit.jupiter.api.Assumptions.assumeTrue @Unroll @@ -445,7 +445,7 @@ abstract class HttpClientTest extends InstrumentationSpecification { final void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", URI uri = resolveAddress("/success"), Integer responseCode = 200) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertClientSpan(OpenTelemetryAssertions.assertThat(spanData), uri, method, responseCode, null) + def assertion = junitTest.assertClientSpan(assertThat(spanData), uri, method, responseCode, null) if (parentSpan == null) { assertion.hasParentSpanId(SpanId.invalid) } else { @@ -456,7 +456,7 @@ abstract class HttpClientTest extends InstrumentationSpecification { final void serverSpan(TraceAssert trace, int index, Object parentSpan = null) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertServerSpan(OpenTelemetryAssertions.assertThat(spanData)) + def assertion = junitTest.assertServerSpan(assertThat(spanData)) if (parentSpan == null) { assertion.hasParentSpanId(SpanId.invalid) } else { diff --git a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy index 54bcba8bbc26..51ebea5841f1 100644 --- a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy +++ b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy @@ -9,7 +9,6 @@ import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.api.trace.Span import io.opentelemetry.api.trace.SpanId import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.semconv.NetworkAttributes import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.instrumentation.test.asserts.TraceAssert @@ -17,9 +16,9 @@ import io.opentelemetry.instrumentation.testing.GlobalTraceUtil import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.HttpAttributes +import io.opentelemetry.semconv.NetworkAttributes import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest import io.opentelemetry.testing.internal.armeria.common.HttpMethod import spock.lang.Shared @@ -28,12 +27,8 @@ import spock.lang.Unroll import javax.annotation.Nullable import java.util.concurrent.Callable -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.* +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat import static org.junit.jupiter.api.Assumptions.assumeFalse import static org.junit.jupiter.api.Assumptions.assumeTrue @@ -455,7 +450,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple void controllerSpan(TraceAssert trace, int index, Object parent, Throwable expectedException = null) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertControllerSpan(OpenTelemetryAssertions.assertThat(spanData), expectedException) + def assertion = junitTest.assertControllerSpan(assertThat(spanData), expectedException) if (parent == null) { assertion.hasParentSpanId(SpanId.invalid) } else { @@ -502,7 +497,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS, String spanID = null) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertServerSpan(OpenTelemetryAssertions.assertThat(spanData), method, endpoint, endpoint.status) + def assertion = junitTest.assertServerSpan(assertThat(spanData), method, endpoint, endpoint.status) if (parentID == null) { assertion.hasParentSpanId(SpanId.invalid) } else { @@ -519,7 +514,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple void indexedServerSpan(TraceAssert trace, int index, Object parent, int requestId) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertIndexedServerSpan(OpenTelemetryAssertions.assertThat(spanData), requestId) + def assertion = junitTest.assertIndexedServerSpan(assertThat(spanData), requestId) if (parent == null) { assertion.hasParentSpanId(SpanId.invalid) } else { @@ -530,7 +525,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple void indexedControllerSpan(TraceAssert trace, int index, Object parent, int requestId) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertIndexedControllerSpan(OpenTelemetryAssertions.assertThat(spanData), requestId) + def assertion = junitTest.assertIndexedControllerSpan(assertThat(spanData), requestId) if (parent == null) { assertion.hasParentSpanId(SpanId.invalid) } else { diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java index 6580189cf90c..8f68cce5c084 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java @@ -5,7 +5,7 @@ package io.opentelemetry.instrumentation.testing; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.awaitility.Awaitility.await; import io.opentelemetry.api.OpenTelemetry; @@ -15,7 +15,6 @@ import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.assertj.MetricAssert; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.assertj.TraceAssert; import io.opentelemetry.sdk.testing.assertj.TracesAssert; import io.opentelemetry.sdk.trace.data.SpanData; @@ -182,9 +181,7 @@ public final void waitAndAssertMetrics( Collection metrics = instrumentationMetrics(instrumentationName); assertThat(metrics).isNotEmpty(); for (Consumer assertion : assertions) { - assertThat(metrics) - .anySatisfy( - metric -> assertion.accept(OpenTelemetryAssertions.assertThat(metric))); + assertThat(metrics).anySatisfy(metric -> assertion.accept(assertThat(metric))); } }); } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpClientTest.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpClientTest.java index 4fc5ac672621..256e90d90bf9 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpClientTest.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpClientTest.java @@ -118,7 +118,10 @@ void successfulGetRequest(String path) throws Exception { testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> assertClientSpan(span, uri, method, responseCode, null).hasNoParent(), + span -> + assertClientSpan(span, uri, method, responseCode, null) + .hasNoParent() + .hasStatus(StatusData.unset()), span -> assertServerSpan(span).hasParent(trace.getSpan(0)))); } @@ -999,7 +1002,9 @@ SpanDataAssert assertClientSpan( // TODO: Move to test knob rather than always treating as optional if (attrs.get(NetworkAttributes.NETWORK_PEER_ADDRESS) != null) { assertThat(attrs) - .containsEntry(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"); + .hasEntrySatisfying( + NetworkAttributes.NETWORK_PEER_ADDRESS, + addr -> assertThat(addr).isIn("127.0.0.1", "0:0:0:0:0:0:0:1")); } if (attrs.get(NetworkAttributes.NETWORK_PEER_PORT) != null) { assertThat(attrs) @@ -1048,7 +1053,7 @@ static SpanDataAssert assertServerSpan(SpanDataAssert span) { return span.hasName("test-http-server").hasKind(SpanKind.SERVER); } - private int doRequest(String method, URI uri) throws Exception { + protected int doRequest(String method, URI uri) throws Exception { return doRequest(method, uri, Collections.emptyMap()); } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestServer.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestServer.java index 0c5644b142d8..dc119084bc24 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestServer.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestServer.java @@ -17,6 +17,7 @@ import io.opentelemetry.instrumentation.test.server.http.RequestContextGetter; import io.opentelemetry.testing.internal.armeria.common.HttpData; import io.opentelemetry.testing.internal.armeria.common.HttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpResponseWriter; import io.opentelemetry.testing.internal.armeria.common.HttpStatus; import io.opentelemetry.testing.internal.armeria.common.ResponseHeaders; import io.opentelemetry.testing.internal.armeria.common.ResponseHeadersBuilder; @@ -29,6 +30,7 @@ import java.nio.file.Paths; import java.security.KeyStore; import java.time.Duration; +import java.util.concurrent.TimeUnit; import javax.net.ssl.KeyManagerFactory; public final class HttpClientTestServer extends ServerExtension { @@ -99,6 +101,24 @@ protected void configure(ServerBuilder sb) throws Exception { "/read-timeout", (ctx, req) -> HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(20))) + .service( + "/long-request", + (ctx, req) -> { + HttpResponseWriter writer = HttpResponse.streaming(); + writer.write(ResponseHeaders.of(HttpStatus.OK)); + writer.write(HttpData.ofUtf8("Hello")); + + ctx.eventLoop() + .schedule( + () -> { + writer.write(HttpData.ofUtf8("World")); + writer.close(); + }, + 1, + TimeUnit.SECONDS); + + return writer; + }) .decorator( (delegate, ctx, req) -> { for (String field : openTelemetry.getPropagators().getTextMapPropagator().fields()) { diff --git a/version.gradle.kts b/version.gradle.kts index 50f3cd2a28d4..62a705c21c34 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,5 +1,5 @@ -val stableVersion = "2.7.0-SNAPSHOT" -val alphaVersion = "2.7.0-alpha-SNAPSHOT" +val stableVersion = "2.8.0-SNAPSHOT" +val alphaVersion = "2.8.0-alpha-SNAPSHOT" allprojects { if (findProperty("otel.stable") != "true") {